Android 自定义控件基础-ListView下拉刷新

  自定义控件学了很久了,发现学了总是忘,于是打算用博客来记录自己学习的知识点。
  今天是自定义ListView来实现下拉刷新,这些文章都是借鉴慕课网上的视频来写的.
  自定义一个控件,先是看它继承于那个控件,如果我们继承View控件的话,那得让我们写很多关于ListView的功能,这些东西我自己觉得很麻烦,而且也没有那个必要因为我们可以直接继承ListView,在listView的基础上来加一些我们需要的东西。

1.向ListView加Header布局

private void initView(Context context)
{
    mLayoutInflater = LayoutInflater.from(context);
    mHeaerView = mLayoutInflater.inflate(R.layout.header_layout, null, false);
    addHeaderView(mHeaerView);
}

2.隐藏Header布局

private void initView(Context context) {
        mLayoutInflater = LayoutInflater.from(context);
        mHeaerView = mLayoutInflater.inflate(R.layout.header_layout, null, false);
        measureView(mHeaerView);
        mHeaderViewHeight = mHeaerView.getMeasuredHeight();
        setHeaderViewHeightPadding(mHeaderViewHeight);
        Log.i("main", mHeaderViewHeight + "");
        addHeaderView(mHeaerView);
    }
    private void measureView(View view)
    {
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        if(lp == null)
        {
            lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        //mHeaerView.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        /**
         * width 和height里面包含的不仅仅有View的宽和高,还有View控件的测量模式
         * 测量模式的产生方式就是如下所示
         */
        int width = ViewGroup.getChildMeasureSpec(0,0,lp.width);
        int height = 0;
        int tempHeight = lp.height;
        if(tempHeight > 0)
        {
            height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
        }
        view.measure(width, height);




    }
    private void setHeaderViewHeightPadding(int padding) {
        mHeaerView.setPadding(mHeaerView.getPaddingLeft(), -padding, mHeaerView.getPaddingRight(), mHeaerView.getPaddingBottom());
        mHeaerView.invalidate();
    }

3.实现ListView的下拉刷新(一)

  要想实现ListView的下拉刷新,必须监听ListView是否滑动到最顶端,因此要实现ListView的监听接口OnScrollListener,并且要监听ListView的OnTouch事件。根据滑动的情况来判断刷新的情况。
  首先我们在定义了一个成员变量来保存ListView的状态--mState
  其次定义了几个静态常量来表示不同的状态

private final static int NONE = 0;  // 无状态
private final static int DOWN_UPDATE = 1; // 提示下拉可以刷新
private final static int UPDATE = 2; // 提示松开可以刷新
private final static int REFLASH = 3; // 更新

最后则是根据不同的滑动来更改mState的状态:

    public boolean onTouchEvent(MotionEvent ev) {
 
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                if (mFirstVisibleItem == 0) {
                    mIsRemark = true;  // mIsRemark只是一个标记,表示当前可见的第一个Item是不是所有的Item中的第一个
                    mStartY = (int) ev.getY();
                    Log.i("main", "我进来了");
                }
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                onMove(ev);
                tempY = (int) (ev.getY() - mStartY);
                Log.i("main", "tempY = " + tempY);
                break;
            }
            case MotionEvent.ACTION_UP: {
 
                if(mState == DOWN_UPDATE)
                {
                    mState = NONE;
                }
                if(mState == UPDATE)
                {
                    mState = REFLASH;
                    mListener.reFlash();
                    Log.i("main", "我来了");
                }
                Log.i("main", "tempY11 = " + tempY);
                if(tempY <= 0 && mIsRemark)
                {
                    Log.i("main", "我进来le");
                    mState = NONE;
                }
 
                change();
                break;
            }
        }
 
 
        return super.onTouchEvent(ev);
    }
 
    private void onMove(MotionEvent ev) {
        if (mIsRemark) {
            if (ev.getY() - mStartY > 0) {
                int dy = (int) (ev.getY() - mStartY);
                if (dy > mHeaderViewHeight + 20) {
                    mState = UPDATE;
                } else {
                    mState = DOWN_UPDATE;
                }
                setHeaderViewHeightPadding(mHeaderViewHeight - dy);
                change();
            }
            return;
        }
        return;
    }
/**<br> *change方法主要是用来处理不同状态下的事件<br> *<br> */
    private void change() {
        initChildView();
        RotateAnimation ani = new RotateAnimation(0, 180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        ani.setDuration(500);
        ani.setFillAfter(true);
        RotateAnimation ani1 = new RotateAnimation(180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        ani1.setDuration(500);
        ani1.setFillAfter(true);
        if (mState == UPDATE)
        {
            mProgressBar.setVisibility(View.GONE);
            mImageView.setVisibility(View.VISIBLE);
            mImageView.clearAnimation();
            mImageView.setAnimation(ani);
            mTextViewFlash.setText("松开可以刷新!");
            mTextViewTime.setVisibility(View.VISIBLE);
            mTextViewTime.setText("上次更新的时间:" + mUpdateTime);
        }
        if (mState == DOWN_UPDATE)
        {
            mProgressBar.setVisibility(View.GONE);
            mImageView.setVisibility(View.VISIBLE);
            mTextViewTime.setVisibility(View.VISIBLE);
            mImageView.clearAnimation();
            mImageView.setAnimation(ani1);
            mTextViewFlash.setText("下拉可以刷新");
            mTextViewTime.setText("上次更新的时间:" + mUpdateTime);
        }
        if (mState == REFLASH)
        {
            setHeaderViewHeightPadding(10);
            mProgressBar.setVisibility(View.VISIBLE);
            mImageView.setVisibility(View.GONE);
            mTextViewTime.setVisibility(View.GONE);
            mTextViewFlash.setText("正在刷新...");
            mImageView.clearAnimation();
        }
        if(mState == NONE)
        {
            Log.i("main", "workspace");
            setHeaderViewHeightPadding(mHeaderViewHeight);
            mIsRemark = false;
            mProgressBar.setVisibility(View.GONE);
            mImageView.setVisibility(View.VISIBLE);
            mImageView.setAnimation(ani1);
        }
    }
    private void initChildView()
    {
        if(mTextViewFlash == null)
        {
            mTextViewFlash = (TextView) mHeaerView.findViewById(R.id.id_textView_Flash);
        }
        if(mTextViewTime == null)
        {
            mTextViewTime = (TextView) mHeaerView.findViewById(R.id.id_textView_Time);
        }
        if(mImageView == null)
        {
            mImageView = (ImageView) mHeaerView.findViewById(R.id.id_imagView);
        }
        if(mProgressBar == null)
        {
            mProgressBar = (ProgressBar) mHeaerView.findViewById(R.id.id_progressbar);
        }
    }

4.实现ListView的下拉刷新(二)

  经过上面的过程,是可以下拉的,处理不同状态下的事件。还有一个问题就是刷新,也就是加载新的数据。加载刷新的操作肯定必须在UI线程中,因此ListView中必须得有一个回调接口,用来MinaActivity来实现,并且来进行一些操作。

回调接口:

public void setOnFlashListener(FlashListener listener)
{
    this.mListener = listener;
}
 
public interface  FlashListener
{
    void reFlash();
}

回调接口的调用:

if(mState == UPDATE)
{
    mState = REFLASH;
    mListener.reFlash();
    Log.i("main", "我来了");
}

MainActivity中回调接口的实现和接口方法的实现:

mListView.setOnFlashListener(new FlashListView.FlashListener() {
            @Override
            public void reFlash() {
                Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        addDatas();
                        loadDatas();
                        mListView.reFalshComplete();
                    }
                }, 5000);
            }
        });
private void addDatas()
   {
       int i = mDatas.size();
       for(int j = i; j < i + 10; j++)
       {
           mDatas.add(new Bean("Title" + j, "Content" + j, R.mipmap.ic_launcher));
       }
       myAdapter.dataChange(mDatas);
   }
   private void loadDatas()
   {
       mListView.setAdapter(myAdapter);
   }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容