package com.wasu.cs.widget.mediacontrol; import java.util.List; import com.wasu.widget.util.UIUtil; import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Rect; import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; /** * 循环滚动控件 */ @SuppressLint("NewApi") public class SidePanelNavigator extends FrameLayout { private static final int ANIMATION_DURATION = 300; private int unselectColor = Color.parseColor("#80ffffff"); private int selectColor = Color.parseColor("#ffffff00"); /** * 当前选中的index */ private int selectedViewIndex; /** * 初始选中默认check的index */ private int checkedViewIndex; public SidePanelNavigator(Context context) { super(context); init(context); } public SidePanelNavigator(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public SidePanelNavigator(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { selectedViewIndex = -1; checkedViewIndex = -1; } public void setNavigatorChildrenModel(List childrenModel) { if (null == childrenModel) return; removeAllViews(); for (int i = 0; i < childrenModel.size(); i++) { INavigatorChildModel model = childrenModel.get(i); LinearLayout child = new LinearLayout(getContext()); child.setOrientation(LinearLayout.VERTICAL); child.setPadding(model.getPaddingLeft(), model.getPaddingTop(), model.getPaddingRight(), model.getPaddingBottom()); child.setTag(model); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0); lp.weight = 1; TextView title = new TextView(getContext()); title.setLayoutParams(lp); title.setText(model.getString()); title.setTextColor(unselectColor); title.setTextSize(TypedValue.COMPLEX_UNIT_PX, model.getTextSize()); title.setGravity(Gravity.CENTER); TextView hint = new TextView(getContext()); hint.setLayoutParams(lp); hint.setText(model.getHintString()); hint.setTextColor(unselectColor); hint.setTextSize(TypedValue.COMPLEX_UNIT_PX, model.getHintTextSize()); hint.setGravity(Gravity.CENTER); hint.setVisibility(View.GONE); child.addView(title); child.addView(hint); FrameLayout.LayoutParams childLp = new FrameLayout.LayoutParams(model.getWidth(), model.getHeight()); childLp.gravity = Gravity.CLIP_VERTICAL; addView(child, childLp); } requestLayout(); } public void clearNavigatorChildrenModel() { this.removeAllViews(); requestLayout(); } public int getSelectedViewIndex() { return selectedViewIndex; } public void setSelectedViewIndex(int index) { if (-1 < index && index < getChildCount()) { checkedViewIndex = index; View v = getChildAt(selectedViewIndex); if (v != null) { onSelectEvent(v, false); v.animate().setDuration(ANIMATION_DURATION).translationY(0).scaleX(1.0f).scaleY(1.0f); } selectedViewIndex = index; v = getChildAt(selectedViewIndex); if (v != null) { onSelectEvent(v, true); v.animate().setDuration(ANIMATION_DURATION).translationY(0).scaleX(1.5f).scaleY(1.5f); } } } public View getSelectedView() { if (-1 == selectedViewIndex) { return null; } return getChildAt(selectedViewIndex); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int viewCount = getChildCount(); if (-1 == selectedViewIndex) { if (0 < getChildCount()) { selectedViewIndex = 0; onSelectEvent(getChildAt(selectedViewIndex), true); } else { super.onLayout(changed, l, t, r, b); return; } } if (0 < viewCount) layoutInLoopMode(changed, l, t, r, b); } private int getLoopItemIndex(int total, int start, int offset) { if (0 == total) return 0; if (offset > 0) { return (start + offset) % total; } else { offset = offset % total; return (start + (total + offset)) % total; } } private void layoutInLoopMode(boolean changed, int l, int t, int r, int b) { View selectedView = getChildAt(selectedViewIndex); int parentCentralX = (b - t) / 2; int selectedViewTop = parentCentralX - (selectedView.getMeasuredHeight() / 2); int totalViewCount = getChildCount(); selectedView.layout(getPaddingLeft(), selectedViewTop, getPaddingRight() + selectedView.getMeasuredWidth(), selectedViewTop + selectedView.getMeasuredHeight()); if (totalViewCount == 1) { return; } int i = 0; int offsetXBottom = selectedViewTop; int processedView = 1; for (;;) { int prevIndex = getLoopItemIndex(totalViewCount, selectedViewIndex, --i); View view = getChildAt(prevIndex); view.layout(getPaddingLeft(), offsetXBottom - view.getMeasuredHeight(), getPaddingRight() + view.getMeasuredWidth(), offsetXBottom); offsetXBottom -= view.getMeasuredHeight(); processedView++; if (processedView >= (totalViewCount / 2)) { break; } } i = 0; int offsetXTop = selectedViewTop + selectedView.getMeasuredHeight(); for (;;) { if (processedView >= totalViewCount) { break; } int nextIndex = getLoopItemIndex(totalViewCount, selectedViewIndex, ++i); View view = getChildAt(nextIndex); view.layout(getPaddingLeft(), offsetXTop, getPaddingRight() + view.getMeasuredWidth(), offsetXTop + view.getMeasuredHeight()); offsetXTop += view.getMeasuredHeight(); processedView++; } } private int focusId = android.R.color.transparent; private boolean drawFocusOnTop = true; public void setFocusResId(int resId) { focusId = resId; } public void setDrawFocusOnTop(boolean top) { drawFocusOnTop = top; } @Override protected void dispatchDraw(Canvas canvas) { if (drawFocusOnTop) { super.dispatchDraw(canvas); drawFocusStatic(canvas); } else { drawFocusStatic(canvas); super.dispatchDraw(canvas); } } private void drawFocusStatic(Canvas canvas) { View view = getSelectedView(); if (null == view) return; Rect rect = new Rect(); // draw shadow and highlighted focus. UIUtil.getViewRect(view,null, rect); UIUtil.drawDrawableAt(canvas, rect, getResources().getDrawable(focusId), true); } public void goNext() { int next = getLoopItemIndex(getChildCount(), selectedViewIndex, 1); if (getChildCount() <= next) { return; } onSelectEvent(getSelectedView(), false); selectedViewIndex = next; requestLayout(); animateNextPrev(true); invalidate(); } public void goPrev() { int prev = getLoopItemIndex(getChildCount(), selectedViewIndex, -1); if (prev < 0) { return; } onSelectEvent(getSelectedView(), false); selectedViewIndex = prev; requestLayout(); animateNextPrev(false); invalidate(); } public void click() { View view = getSelectedView(); if (null != view) { INavigatorChildModel model = (INavigatorChildModel) view.getTag(); setSelectedViewIndex(getSelectedViewIndex()); if (null != model) { model.onClick(); } } } private void onSelectEvent(final View view, boolean selected) { ViewGroup parent = (ViewGroup) view; if (null != parent) { INavigatorChildModel model = (INavigatorChildModel) parent.getTag(); TextView title = (TextView) parent.getChildAt(0); TextView hint = (TextView) parent.getChildAt(1); if (selected) { if (selectedViewIndex == checkedViewIndex) { if (!TextUtils.isEmpty(model.getHintString().trim())) { hint.setVisibility(View.VISIBLE); title.setText(model.getString()); } } // tx.setShadowLayer(20.f, 0.f, 0.f, Color.YELLOW); title.setTextColor(selectColor); hint.setTextColor(selectColor); } else { if (selectedViewIndex == checkedViewIndex) { if (!TextUtils.isEmpty(model.getHintString().trim())) { hint.setVisibility(View.GONE); title.setText(model.getHintString() + model.getString()); } } // tx.setShadowLayer(0.f, 0.f, 0.f, Color.WHITE); title.setTextColor(unselectColor); hint.setTextColor(unselectColor); } } } private void animateNextPrev(final boolean next) { final ViewTreeObserver observer = this.getViewTreeObserver(); observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { getViewTreeObserver().removeOnPreDrawListener(this); int totalViewCount = getChildCount(); View toView = getChildAt(selectedViewIndex); View fromView = null; int delta = 0; if (next) { // / We are going up to highlight next. fromView = getChildAt(getLoopItemIndex(totalViewCount, selectedViewIndex, -1)); if (fromView != null) { delta = -(fromView.getMeasuredHeight() + toView.getMeasuredHeight()) / 2; } } else { // / We are going down to highlight prev. fromView = getChildAt(getLoopItemIndex(totalViewCount, selectedViewIndex, 1)); if (fromView != null) { delta = (fromView.getMeasuredHeight() + toView.getMeasuredHeight()) / 2; } } boolean isFirstAnimation = true; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); // / We need to change back to the previous state. child.setTranslationY(-delta); if (child == toView) { child.animate().setDuration(ANIMATION_DURATION).translationY(0).scaleX(1.5f).scaleY(1.5f); } else if (child == fromView) { child.animate().setDuration(ANIMATION_DURATION).translationY(0).scaleX(1.0f).scaleY(1.0f); } else { child.animate().setDuration(ANIMATION_DURATION).translationY(0); } if (isFirstAnimation) { child.animate().setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(android.animation.Animator animation) { onSelectEvent(getSelectedView(), true); invalidate(); }; }); } isFirstAnimation = false; } return true; } }); } public void refresh() { for (int i = 0; i < getChildCount(); i++) { ViewGroup parent = (ViewGroup) getChildAt(i); INavigatorChildModel model = (INavigatorChildModel) parent.getTag(); TextView title = (TextView) parent.getChildAt(0); TextView hint = (TextView) parent.getChildAt(1); title.setText(model.getString()); hint.setText(model.getHintString()); } } public interface INavigatorChildModel { int getWidth(); int getHeight(); int getPaddingLeft(); int getPaddingTop(); int getPaddingRight(); int getPaddingBottom(); int getTextSize(); int getHintTextSize(); String getString(); String getHintString(); void onClick(); } }