Android LinearLayout实现下拉刷新

Posted 一口仨馍

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android LinearLayout实现下拉刷新相关的知识,希望对你有一定的参考价值。

效果图

实现思路

一般刷新给ListView设置addHeaderView,这里我们可以模仿着这种方式实现可刷新的LinearLayout。然而LinearLayout并没有提供addHeaderView方法,既然不提供那么我们自己addView添加第一个View为我们下拉刷新的布局不就成了嘛。这里将添加的第一个View称为HeadView。初始化的时候隐藏HeadView,然后重写onTouchEvent(),实现自己的下拉刷新逻辑。如果想增加自己的一些动画,修改代码添加上动画的代码即可。开打!开打!

RefreshLinearLayout的具体实现

/**
 * Created by dongyk on 2016/6/24.
 */
public class RefreshLinearLayout extends LinearLayout 
    private final String TAG = this.getClass().getSimpleName();

    private Context mContext;
    private View mHeadView;
    private TextView mTv;

    private int mHeadViewHeight = 50;//单位dp
    private int mStartX;
    private int mStartY;
    private int mEndX;
    private int mEndY;
    private int dx;
    private int dy;

    /**
     * 是否正在刷新标志位
     */
    private boolean isRefresh;

    public RefreshLinearLayout(Context context) 
        this(context,null);
    

    public RefreshLinearLayout(Context context, AttributeSet attrs) 
        this(context, attrs,0);
    

    public RefreshLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        mContext = context;
        forceVertival();
        addHeadView();
        onComplete();
    

    /**
     * 强制设置垂直方向
     */
    private void forceVertival() 
        if (getOrientation() == LinearLayout.HORIZONTAL)
            setOrientation(LinearLayout.VERTICAL);
        
    

    /**
     * 增加头布局文件
     */
    private void addHeadView() 
        LayoutInflater inflater = LayoutInflater.from(mContext);
        mHeadView = inflater.inflate(R.layout.head_refresh_linearlayout,null);
        mHeadViewHeight = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, (float) mHeadViewHeight, mContext.getResources().getDisplayMetrics());
        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mHeadViewHeight);
        mHeadView.setLayoutParams(params);
        this.addView(mHeadView);
        mTv = (TextView)(mHeadView.findViewById(R.id.tv_head_refresh));
    

    /**
     * 隐藏HeadView
     */
    public void onComplete()
        setHeadViewPaddingTop(mHeadViewHeight);
    

    /**
     *
     * @param paddingTop 隐藏的高度
     */
    private void setHeadViewPaddingTop(int paddingTop)
        if(paddingTop > mHeadViewHeight)
            new IllegalArgumentException("paddingTop must < HeadViewHeight ");
        
        setPadding(getPaddingLeft(),-paddingTop,getPaddingRight(),getPaddingBottom());
    

    @Override
    public boolean onTouchEvent(MotionEvent event) 
        switch (event.getAction())
            case MotionEvent.ACTION_DOWN:
                mStartX = (int) event.getX();
                mStartY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                mEndX = (int) event.getX();
                mEndY = (int) event.getY();
                dx = Math.abs(mEndX - mStartX);
                dy = mEndY - mStartY;
                if (0 < dy && dy < mHeadViewHeight && dy>dx)
                    setHeadViewPaddingTop(mHeadViewHeight - dy );
                    mTv.setText("下拉加载数据");
                else if(dy >= mHeadViewHeight && dy>dx)
                    setHeadViewPaddingTop(0);
                    isRefresh = true;
                    mTv.setText("松开即可刷新");
                
                break;
            case MotionEvent.ACTION_UP:
                if (isRefresh) 
                    setHeadViewPaddingTop(0);
                    mTv.setText("正在刷新");
                    if (mIRefreshListener != null)
                        mIRefreshListener.onStart();
                        mIRefreshListener.onLoading();
                        mIRefreshListener.onEnd();
                    
                    isRefresh = false;
                 else 
                    onComplete();
                
                break;
            default:
                break;
        
        return true;
    

    private IRefreshListener mIRefreshListener;

    public interface IRefreshListener
        void onStart();
        void onLoading();
        void onEnd();
    

    public void setIRefreshListener(IRefreshListener mIRefreshListener) 
        this.mIRefreshListener = mIRefreshListener;
    

控件取名为RefreshLinearLayout。可以看到RefreshLinearLayout继承LinearLayout。在三个互相调用的构造函数最后我们首先设置RefreshLinearLayout的布局方向为VERTICAL,如果是横向,应该是左滑刷新,下拉刷新难免太傻了点。之后填充HeadView设置参数没撒好说的。花3s扫一下即可。紧接着调用了onComplete()方法,在暴露的公共方法onComplete()方法中调用setHeadViewPaddingTop()参数为HeadView的高mHeadViewHeight。需要注意下这个方法。

    /**
     *
     * @param paddingTop 隐藏的高度
     */
    private void setHeadViewPaddingTop(int paddingTop)
        if(paddingTop > mHeadViewHeight)
            new IllegalArgumentException("paddingTop must < HeadViewHeight ");
        
        setPadding(getPaddingLeft(),-paddingTop,getPaddingRight(),getPaddingBottom());
    

可以看到setHeadViewPaddingTop()中仅仅调用了setPadding()方法。

setPadding(getPaddingLeft(),-paddingTop,getPaddingRight(),getPaddingBottom());

由于下拉刷新的存在,所以这里禁止了布局文件中android:paddingTop=""的属性。而且在paddingTop的位置,设置的是参数的负值。正常设置padding是正值,因此产生内容和控件的距离。如果padding是负值,那么显示效果为内容大于控件,多余部分则会被隐藏。由于给setHeadViewPaddingTop()传递的参数为HeadViewHeight,因此实现了隐藏HeadView的效果。

然后再onTouchEvent()中随着手势不断改变HeadView隐藏的高度,即可达到下拉刷新的效果。在ACTION_UP中根据标志位isRefresh判断是否执行相应的操作。为此还需要一些回调方法,这里统一写在IRefreshListener接口中。

    private IRefreshListener mIRefreshListener;

    public interface IRefreshListener
        void onStart();
        void onLoading();
        void onEnd();
    

    public void setIRefreshListener(IRefreshListener mIRefreshListener) 
        this.mIRefreshListener = mIRefreshListener;
    

ACTION_UP中依次调用了onStart()onLoading()onEnd()方法。除此之外,还暴露了一个onComplete()方法,用于收起HeadView。至此,RefreshLinearLayout分析完毕。下面看个简单实现的Demo。

运用示例

    private void initRefreshListener() 
        ll_refresh.setIRefreshListener(new RefreshLinearLayout.IRefreshListener() 
            @Override
            public void onStart() 
                Log.i("RefreshLinearLayout", "onStart");
                tv.setText("onStart");
            

            @Override
            public void onLoading() 
                Log.i("RefreshLinearLayout", "onLoading");
                tv.setText("onLoading");
                 getNetWorkData();
            

            @Override
            public void onEnd() 
                tv.setText("onEnd");
                Log.i("RefreshLinearLayout", "onEnd");
            
        );
    

    /**
     * 模拟网络获取数据
     */
    private void getNetWorkData()
        new Thread(new Runnable() 
            @Override
            public void run() 
                try 
                    Thread.sleep(2*1000);
                    myHandler.sendEmptyMessage(EMPTY_MSG);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        ).start();
    

    private MyHandler myHandler;

    class MyHandler extends Handler
        @Override
        public void handleMessage(Message msg) 
            super.handleMessage(msg);
            if (msg.what == EMPTY_MSG)
                ll_refresh.onComplete();
            
        
    

onLoading()中执行getNetWorkData()方法模拟网络请求,在Handler中调用onComplete()方法收起HeadView。这里的Handler可能会造成内存泄露,完善点可以在onDestroy()中添加myHandler.removeCallbacksAndMessages(null)。感兴趣可以详见内存泄露简介、典型情景及检测解决

以上是关于Android LinearLayout实现下拉刷新的主要内容,如果未能解决你的问题,请参考以下文章

android - 过度绘制布局允许通过 LinearLayout 进行触摸

SwipeRefreshLayout + RecyclerView 实现 上拉刷新 和 下拉刷新

android LinearLayout中嵌套一个textview,当textview更新时会触发LinearLayout也更新。用啥办法可以让

Android学习——LinearLayout布局实现居中左对齐右对齐

Android中ExpandableListView的使用

Android动画下拉/向上视图正确