实现类似天猫列表消息自动垂直滚动效果
2016-05-31 22:00
555 查看
一、先看效果
前段时间在交流群里看到有小伙伴在问一个消息滚动列表怎么做,正好最近在学校准备毕业答辩,公司请了两周假,忙里偷闲把这个效果实现了,整体感觉还是不错的,代码量也比较少,练练手的同时也给小伙伴们分享一下。先上图:二、实现原理
就这个效果第一眼看到的时候有点想用自定View来写,感觉有点像歌词翻动的效果,不过思考最后还是没有用这个方案,主要是实现起来有点麻烦,而且这个效果用自定义ViewGroup来做更为方便,逻辑也更清晰。整个控件为一个继承自FramLayout的ViewGroup,内部放置两个子控件,用于视图的切换,为了响应外部点击事件,我用了两个Button作为子控件,当然也可以用TextView。在初始化时布局内部两个子控件的位置,第一个子控件位于第二个子控件的上方,
getChildAt(0).layout(0,0,mWidth,mHeight); getChildAt(1).layout(0,mHeight,mWidth,mHeight*2);
而我们的整个控件大小与子控件大小一致。之后滚动整个控件,这时也就形成了第一个控件往上走而第二个控件在后面跟着的效果,在下方子控件完全显示在界面中间时我们要做的处理是重新布局整个页面,将上部不可见的子控件调到显示控件的下方,并在这个时候改变不可见控件的内容,用OnPreTextChangeListener来供外部改变子控件的内容,需要注意的是,子控件默认都应该是TextView或者其子类型。以此实现切换效果,这一步我是这么做的:
//改变设置布局参数 为了显示停顿效果 这里延迟了3秒滚动 ischange = !ischange; postDelayed(new Runnable() { @Override public void run() { mScroller.startScroll(0, 0, 0, mHeight, 2000); scrollTo(0, 0); requestLayout(); if(getChildAt(0).getBottom() == mHeight){ mlistener.SetOnPreTextChangeListener((TextView) getChildAt(0)); }else if(getChildAt(1).getBottom() == mHeight){ mlistener.SetOnPreTextChangeListener((TextView) getChildAt(1)); } postInvalidate(); } },3000); @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // Log.e("TAG","onLayout"); //来回切换内部两个控件的位置 形成来回滑动效果 if(ischange){ getChildAt(1).layout(0,0,mWidth,mHeight); getChildAt(0).layout(0,mHeight,mWidth,mHeight*2); }else{ getChildAt(0).layout(0,0,mWidth,mHeight); getChildAt(1).layout(0,mHeight,mWidth,mHeight*2); } }
最后,在界面从window移除是做点扫尾工作:
@Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mScroller.abortAnimation(); getHandler().removeCallbacksAndMessages(null); }
三、具体实现
/** 改变布局*/ private boolean ischange = false; /** */ private Scroller mScroller; /** 高度*/ private int mHeight ; /** 宽度*/ private int mWidth ; /** 文字改变前的监听 在这里给控件设置内容*/ private OnPreTextChangeListener mlistener; public void setOnPreTextChangeListener(OnPreTextChangeListener mlistener){ this.mlistener = mlistener; } public RollView(Context context) { this(context, null); } public RollView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RollView(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public RollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mScroller = new Scroller(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获取控件宽高 mHeight = getChildAt(0).getMeasuredHeight(); mWidth = getChildAt(0).getMeasuredWidth(); } @Override public void computeScroll() { //判断scroller是否滚动结束 if (mScroller.computeScrollOffset()){ scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); }else if (getScrollY() == mHeight){ //改变设置布局参数 ischange = !ischange; postDelayed(new Runnable() { @Override public void run() { mScroller.startScroll(0, 0, 0, mHeight, 2000); scrollTo(0, 0); requestLayout(); if(getChildAt(0).getBottom() == mHeight){ mlistener.SetOnPreTextChangeListener((TextView) getChildAt(0)); }else if(getChildAt(1).getBottom() == mHeight){ mlistener.SetOnPreTextChangeListener((TextView) getChildAt(1)); } postInvalidate(); } },3000); }else if(getScrollY() == 0){ //第一次进来 postDelayed(new Runnable() { @Override public void run() { mScroller.startScroll(0, 0, 0, mHeight,2000); postInvalidate(); } },3000); } } public interface OnPreTextChangeListener{ void SetOnPreTextChangeListener(TextView tv); } @Override protected void onFinishInflate() { super.onFinishInflate(); if(getChildCount() != 2){ new IllegalArgumentException("must be has 2 children."); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mScroller.abortAnimation(); getHandler().removeCallbacksAndMessages(null); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // Log.e("TAG","onLayout"); //来回切换内部两个控件的位置 形成来回滑动效果 if(ischange){ getChildAt(1).layout(0,0,mWidth,mHeight); getChildAt(0).layout(0,mHeight,mWidth,mHeight*2); }else{ getChildAt(0).layout(0,0,mWidth,mHeight); getChildAt(1).layout(0,mHeight,mWidth,mHeight*2); } }
mainactivity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private int num = 0; private String[] mStrData = {"苏宁看不下去,全场免费送!","58同城来凑热闹!","京东大促,全场5元!","天猫不服,全场不要钱!"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RollView rv = (RollView) findViewById(R.id.rollview); Button mbtn_jingdong = (Button) findViewById(R.id.jingdong); Button mbtn_tianmao = (Button) findViewById(R.id.tianmao); mbtn_jingdong.setOnClickListener(this); mbtn_tianmao.setOnClickListener(this); rv.setOnPreTextChangeListener(new RollView.OnPreTextChangeListener() { @Override public void SetOnPreTextChangeListener(TextView tv) { if(num >= mStrData.length){ num = 0; } tv.setText(mStrData[num]); num++; } }); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.jingdong: case R.id.tianmao: Toast.makeText(this,((TextView)v).getText().toString().trim(),Toast.LENGTH_SHORT).show(); break; } } }
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <path.example.com.mycustom.RollView android:id="@+id/rollview" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" > <Button android:id="@+id/jingdong" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="京东大促,全场5元!" android:textColor="#f60" android:drawableLeft="@mipmap/tm_rate_icon_star_full" android:background="#e2e2e2" android:textSize="20sp"/> <Button android:id="@+id/tianmao" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="天猫不服,全场不要钱!" android:textColor="#f60" android:drawableLeft="@mipmap/tm_rate_icon_star_full" android:background="#e2e2e2" android:textSize="20sp"/> </path.example.com.mycustom.RollView> </RelativeLayout>
整体实现相对不是很复杂,主要有几点:动态的给子控件布局和赋值,在整体实现过程中用到了Scroller这个辅助类,下面就简单讲下这个辅助类的使用。
四、Scroller
Scroller本身不能让控件滚动,要让控件滚动要用到View的computeScroll方法,在computeScroll方法中进行判断是否滚动结束,对控件做相应的处理。Scroller的几个主要方法://开始滚动 startX startY 开始的X,Y值 dx dy 水平和垂直位置滚动的距离 duration 滚动所用的时间 默认为250毫秒 public void startScroll(int startX, int startY, int dx, int dy, int duration) ; //是否滚动到指定位置 public boolean computeScrollOffset(); //是否滚动完成 public final boolean isFinished() ; //中断动画 cause the scroller to move to the final x and y position public void abortAnimation() ;
源码下载:https://github.com/zhangke3016/CustomRollView
相关文章推荐
- POJ1364 King
- 文件MD5校验
- 个人工作总结05(第二次冲刺)
- 01背包问题
- 机器学习/CNN系列小问题(1):逻辑回归和神经网络之间有什么关系?
- 南通大学毕业设计(论文)答辩记录
- ios开发UI篇—在ImageView中添加按钮以及Tag的参数说明
- paper 73 :HDR(High Dynamic Range Imaging)在摄影中指高动态范围成像
- 6.通过反射动态创建对象,获取属性,获取方法
- AFNetworking3.1 基本使用
- 【Java基础一】String创建对象"" && null
- Retrofit结合RxJava的一次实践
- java.lang.String cannot be cast to org.springframework.http.MediaType 异常分析
- Install and test nginx on Ubuntu 14.04
- 简单的网络技术及数据解析
- lua脚本语言在WireShark中的使用浅析
- 学习第二天
- Android面试点总结
- https -- 超文本传输安全协议
- SQL --NULL的注意事项