前言:
此贴在于功能,粘贴可用、阅读可懂,代码优化和UI请按照
需求自行定义。
主要功能:
1.支持自定义轮播布局
2.支持自定义轮播效果
3.支持单/多行轮播显示,随意定制显示条目
GIF:
实现思路:
垂直跑马灯,定时轮播,继承ViewFlipper实现。此贴没有对ViewFlipper进行详细的源码解析,如有需求请自行查阅。
Begin: ...
1、创建XMarqueeView类继承自ViewFlipper
public class XMarqueeView extends ViewFlipper implements XMarqueeViewAdapter.OnDataChangedListener { /** * 是否设置动画时间间隔 */ private boolean isSetAnimDuration = false; /** * 是否单行显示 */ private boolean isSingleLine = true; /** * 轮播间隔 */ private int interval = 3000; /** * 动画时间 */ private int animDuration = 1000; private int textSize = 14; private int textColor = Color.parseColor("#888888"); /** * 一次性显示多少个 */ private int itemCount = 1; private XMarqueeViewAdapter mMarqueeViewAdapter; public XMarqueeView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs, 0); } private void init(Context context, AttributeSet attrs, int defStyleAttr) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.XMarqueeView, defStyleAttr, 0); if (typedArray != null) { isSetAnimDuration = typedArray.getBoolean(R.styleable.XMarqueeView_isSetAnimDuration, false); isSingleLine = typedArray.getBoolean(R.styleable.XMarqueeView_isSingleLine, true); interval = typedArray.getInteger(R.styleable.XMarqueeView_marquee_interval, interval); animDuration = typedArray.getInteger(R.styleable.XMarqueeView_marquee_animDuration, animDuration); if (typedArray.hasValue(R.styleable.XMarqueeView_marquee_textSize)) { textSize = (int) typedArray.getDimension(R.styleable.XMarqueeView_marquee_textSize, textSize); textSize = PxDpUtils.px2sp(context, textSize); } textColor = typedArray.getColor(R.styleable.XMarqueeView_marquee_textColor, textColor); itemCount = typedArray.getInt(R.styleable.XMarqueeView_marquee_count, itemCount); typedArray.recycle(); } isSingleLine = itemCount == 1; Animation animIn = AnimationUtils.loadAnimation(context, R.anim.anim_marquee_in); Animation animOut = AnimationUtils.loadAnimation(context, R.anim.anim_marquee_out); if (isSetAnimDuration) { animIn.setDuration(animDuration); animOut.setDuration(animDuration); } setInAnimation(animIn); setOutAnimation(animOut); setFlipInterval(interval); } public void setAdapter(XMarqueeViewAdapter adapter) { if (adapter == null) { throw new RuntimeException("adapter must not be null"); } if (mMarqueeViewAdapter != null) { throw new RuntimeException("you have already set an Adapter"); } this.mMarqueeViewAdapter = adapter; mMarqueeViewAdapter.setOnDataChangedListener(this); setData(); } private void setData() { removeAllViews(); int currentIndex = 0; int loopconunt = mMarqueeViewAdapter.getItemCount() % itemCount == 0 ? mMarqueeViewAdapter.getItemCount() / itemCount : mMarqueeViewAdapter.getItemCount() / itemCount + 1; for (int i = 0; i < loopconunt; i++) { if (isSingleLine) { View view = mMarqueeViewAdapter.onCreateView(this); mMarqueeViewAdapter.onBindView(view, view, currentIndex); currentIndex = currentIndex + 1; addView(view); } else { LinearLayout parentView = new LinearLayout(getContext()); parentView.setOrientation(LinearLayout.VERTICAL); parentView.setGravity(Gravity.CENTER); parentView.removeAllViews(); for (int j = 0; j < itemCount; j++) { View view = mMarqueeViewAdapter.onCreateView(this); parentView.addView(view); mMarqueeViewAdapter.onBindView(parentView, view, getRealPosition(j, currentIndex)); currentIndex = getRealPosition(j, currentIndex); } addView(parentView); } } startFlipping(); } private int getRealPosition(int index, int currentIndex) { if ((index == 0 && currentIndex == 0) || (currentIndex == mMarqueeViewAdapter.getItemCount() - 1 && currentIndex % itemCount == 0)) { return 0; } else { return currentIndex + 1; } } @Override public void onChanged() { setData(); } @Override protected void onVisibilityChanged(@NonNull View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if (VISIBLE == visibility) { startFlipping(); } else if (GONE == visibility || INVISIBLE == visibility) { stopFlipping(); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); startFlipping(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); stopFlipping(); } }
2、在res文件夹下创建anim 文件夹,添加动画:
anim_marquee_in.xml
xmlns:android="http://schemas.android.com/apk/res/android">
android:duration="300"
android:fromYDelta="100%p"
android:toYDelta="0"/>
android:duration="500"
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
anim_marquee_out.xml
xmlns:android="http://schemas.android.com/apk/res/android">
android:duration="400"
android:fromYDelta="0"
android:toYDelta="-100%p"/>
android:duration="500"
android:fromAlpha="1.0"
android:toAlpha="0.0"/>
3、创建XMarqueeViewAdapter抽象类
public abstract class XMarqueeViewAdapter<T> { protected List<T> mDatas; private OnDataChangedListener mOnDataChangedListener; public XMarqueeViewAdapter(List<T> datas) { this.mDatas = datas; if (datas == null || datas.isEmpty()) { throw new RuntimeException("Nothing to Show With XMarqueeView"); } } public void setData(List<T> datas) { this.mDatas = datas; notifyDataChanged(); } public int getItemCount() { return mDatas == null ? 0 : mDatas.size(); } public abstract View onCreateView(XMarqueeView parent); public abstract void onBindView(View parent, View view, int position); public void setOnDataChangedListener(OnDataChangedListener onDataChangedListener) { mOnDataChangedListener = onDataChangedListener; } public void notifyDataChanged() { if (mOnDataChangedListener != null) { mOnDataChangedListener.onChanged(); } } public interface OnDataChangedListener { void onChanged(); } }
4、需要用到一个Utils和attrs文件
/** * 常用工具类 */ public class PxDpUtils { // 将px值转换为dip或dp值 public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } // 将dip或dp值转换为px值 public static int dip2px(Context context, float dipValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dipValue * scale + 0.5f); } // 将px值转换为sp值 public static int px2sp(Context context, float pxValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (pxValue / fontScale + 0.5f); } // 将sp值转换为px值 public static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } // 屏幕宽度(像素) public static int getWindowWidth(Activity context) { DisplayMetrics metric = new DisplayMetrics(); context.getWindowManager().getDefaultDisplay().getMetrics(metric); return metric.widthPixels; } // 屏幕高度(像素) public static int getWindowHeight(Activity context) { DisplayMetrics metric = new DisplayMetrics(); context.getWindowManager().getDefaultDisplay().getMetrics(metric); return metric.heightPixels; } }
name="XMarqueeView"> name="isSetAnimDuration" format="boolean"/> name="isSingleLine" format="boolean"/> name="marquee_count" format="integer"/> name="marquee_interval" format="integer|reference"/> name="marquee_animDuration" format="integer|reference"/> name="marquee_textSize" format="dimension|reference"/> name="marquee_textColor" format="color|reference"/>
xml布局就不发上来了,直接调用刚才自定义的XMarqueeView就可以
6、最后上MainActivity的代码:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); xMarqueeView = findViewById(R.id.xMarqueeView); btn_change = findViewById(R.id.btn_changeData); EvalData = new ArrayList<>(); EvalLogoList = new ArrayList<>(); EvalData.add("old data1"); EvalData.add("old data2"); EvalLogoList.add("http://pic.616pic.com/ys_b_img/00/66/73/9KnqqgZBFe.jpg"); EvalLogoList.add("http://pic.616pic.com/ys_b_img/00/69/98/PbzuWBVtkr.jpg"); adapter = new MarqueeViewAdapter(EvalLogoList, EvalData, MainActivity.this); xMarqueeView.setAdapter(adapter); btn_change.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EvalData.clear(); EvalLogoList.clear(); EvalData.add("修改后的数据1111"); EvalData.add("修改后的数据2222"); EvalLogoList.add("http://pic.616pic.com/ys_b_img/00/68/81/Z69qGomd7n.jpg"); EvalLogoList.add("http://pic.616pic.com/ys_b_img/00/70/98/s0qjlzyiPU.jpg"); adapter.notifyDataChanged(); } }); }整个流程就是这样,如有疑问请留言。END...