android案例---下拉刷新

作者: return_toLife | 来源:发表于2017-08-09 15:05 被阅读67次

    通过这个文章你可以学习到:

    1. listview的使用

    2. 触摸事件监听

    3. 回调函数的简单应用

    4. 线程的使用

    5. 最后肯定是实现下拉刷新啦

    先让我们看看最终效果

    GIF.gif

    那么我们就开始吧

    1.因为要用到自定义的listview,我们这里先新建一个view继承listview
    (ps:如果要在XML配置该View的话,我们至少要实现前面两个构造方法)

    public class MyListView extends ListView {
    
        public MyListView(Context context) {
            super(context);
        }
    
        public MyListView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    }
    

    2.创建一个listview的item布局,然后我们再主布局文件中使用我们自己的View
    (ps:listview_item.xml,该布局可以自由扩展,我这里就用最简单的)

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:gravity="center_vertical">
    
     <ImageView
         android:layout_marginLeft="10dp"
         android:id="@+id/listview_image"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:src="@mipmap/ic_launcher"/>
     <TextView
         android:paddingTop="15dp"
         android:layout_marginLeft="10dp"
         android:id="@+id/listview_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="111"
         android:textSize="20dp"/>
    </LinearLayout>
    

    (ps:activity_main)

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.a455.mypurefresh.MainActivity">
    
    <com.example.a455.mypurefresh.MyListView
        android:id="@+id/myview"
        android:layout_width="368dp"
        android:layout_height="wrap_content"
        tools:layout_editor_absoluteY="0dp"
        tools:layout_editor_absoluteX="8dp">
    </com.example.a455.mypurefresh.MyListView>
    </android.support.constraint.ConstraintLayout>
    

    3.我们给listview添加适配器,这里使用的simpleadapter
    (ps:我这里就直接贴代码,很简单的,就是调用了三个简单的方法)

       public class MainActivity extends AppCompatActivity {
    
        MyListView myListView;
    
        SimpleAdapter simpleAdapter;
    
        List<Map<String,Object>> data;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            findView();
    
            initData();
    
            initListView();
        }
    
        /**
        * @methodName: initListView
        * @Description: listview添加设配器
        * @param
        * @return
        * @throws
        */
        private void initListView() {
    
              simpleAdapter=new SimpleAdapter(
                    this,                                //上下文
                    data,                                //数据集
                    R.layout.listview_item,             //item布局文件
                    new String[]{"text","image"},       //map集合中的键值
                    new int[]{R.id.listview_text,R.id.listview_image}  //item布局文件中的控件id
    
            myListView.setAdapter(simpleAdapter);
        }
    
    
        /**
        * @methodName: initData
        * @Description: 对数据初始化
        * @param
        * @return
        * @throws
        */
        private void initData() {
            data=new ArrayList<>();
            for (int i = 0; i < 20; i++) {
                Map<String,Object> map=new HashMap<String,Object>();
                map.put("text",i);
                map.put("image",R.mipmap.ic_launcher);
                data.add(map);
            }
        }
    
        private void findView() {
            myListView=(MyListView)findViewById(R.id.myview);
        }
    

    到这里我们就显示出一个列表了,前面都是配菜,现在开始主菜

    4. 先创建我们的下拉头部布局文件
    (ps:header.xml,这里主要注意的是progressBar中的visibility:gone
    gone意味隐藏并且不占用空间)

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <RelativeLayout
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <LinearLayout
                android:orientation="vertical"
                android:layout_centerInParent="true"
                android:id="@+id/layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">
                <TextView
                    android:id="@+id/tip"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="下拉可刷新"/>
                <TextView
                    android:layout_marginTop="5dp"
                    android:id="@+id/time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
            </LinearLayout>
    
            <ImageView
                android:id="@+id/header_image"
                android:layout_marginRight="10dp"
                android:src="@drawable/pull_down"
                android:layout_toLeftOf="@id/layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
    
            <ProgressBar
                android:id="@+id/progressbar"
                android:layout_marginRight="10dp"
                android:layout_toLeftOf="@id/layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="?android:attr/progressBarStyleSmall"
                android:visibility="gone"/>
        </RelativeLayout>
    </LinearLayout>
    
    

    5. ok,头部文件有了,那么我们要把它加到我们自定义view的顶部
    (这里我就写关键代码啦,记得在构造方法中调用该方法

      View header;
       int headerHight;
     private  void init(Context context) {
                 //解析header布局文件
            header = LayoutInflater.from(context).inflate(R.layout.header, null);
    
            //测量header的高宽,具体可以查看MeasureSpec包
            int width = View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            int hight = View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            header.measure(width, hight);
    
            //获取头部的高度,用于后面隐藏我下拉
            headerHight=header.getMeasuredHeight();
            //将header添加到listview的顶部
            this.addHeaderView(header);
        }
    

    那我们的布局现在是这样的:


    111.PNG

    我们用一个方法把它隐藏:

    
        /**
          * 根据传入的高度设置header的paddingtop
          */
        private void toPadding(int i) {
            header.setPadding(
                    header.getPaddingLeft(),
                    i,
                    header.getPaddingRight(),
                    header.getPaddingBottom()
            );
            header.invalidate();
        }
    

    然后我们只需要在addHeaderView前面调用该方法

           //隐藏头部文件
            toPadding(-headerHight);
    
            //将header添加到listview的顶部
            this.addHeaderView(header);
    

    6. 那么接下来我们就是该监听触摸下拉的事件了,这里是难点
    (ps:首先添加滑动监听接口,重写它要实现的方法,定义两个变量来存储信息
    不要忘了添加回调接口哟)

    public class MyListView extends ListView implements AbsListView.OnScrollListener{
        int scrollState;  //当前view的滚动状态
    
        int firstVisibleItem; //可见的第一个item 
    
          .....//之前的内容省略啦
     private  void init(Context context) {
       .....//之前的内容省略啦
       this.setOnScrollListener(this);  
    }
      @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            this.scrollState=scrollState;
        }
    
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            this.firstVisibleItem=firstVisibleItem;
        }
    

    然后我们再添加几个变量用来表示下拉的状态:

    public class MyListView extends ListView implements AbsListView.OnScrollListener{
       int startY;     //触摸屏幕时的高度
        boolean isRemak;   //是否可以刷新
        int state=0;            //当前header状态
        final int NONE=0;     //正常状态
        final int PULL=1;     //下拉状态
        final int RELEASE=2;  //提示刷新状态
        final int REFRESHING=3; //正在刷新状态
        ......
    

    重头戏来啦,我们重写onTouche方法:

    
        /*
        *触摸事件处理,分三种情况,按下,移动,抬起
        * 按下:判断当前是否为listview顶部,如果是记录按下位置
        * 移动:调用onMove方法处理移动情况
        * 抬起:如果当前状态为提示刷新状态就对header进行更新,并调用刷新内容接口
         */
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
    
            switch (ev.getAction()){
                case  MotionEvent.ACTION_DOWN:
                    if(firstVisibleItem==0){
                        isRemak=true;
                        startY=(int)ev.getY();
                    }
                    break;
                case  MotionEvent.ACTION_MOVE:
                    onMove(ev);      //判断state的状态
                    break;
                case  MotionEvent.ACTION_UP:
                    if(state==RELEASE){
                        state=REFRESHING;
                        refreshByState();//刷新header
                        //iRefreshen.onRefresh(); //刷新listview,这里是通过接口回调的方法来实现的我们先注释掉,后面再来实现
                    }else if(state==PULL){
                        state=NONE;
                        isRemak=false;
                        refreshByState();
                    }
                    break;
            }
    
            return super.onTouchEvent(ev);
    
        }
    

    onMove方法是监听当我们再滑动的时候,我们的state是处于什么状态

      /*
        *触摸移动的时候判断移动的位置
        * 若下拉高过或低于设定的数值,则改变提示信息
         */
        private void onMove(MotionEvent ev) {
    
            if(!isRemak){
                return;
            }
    
            int tempY=(int) ev.getY();
            int distance=tempY-startY;
            int toPadding=distance-headerHight;
    
            switch (state){
                case NONE:
                    if(distance>0){
                        state=PULL;
                        refreshByState();
                    }
                    break;
                case PULL:
                    toPadding(toPadding);
                    if(distance>(headerHight+150) && scrollState==SCROLL_STATE_TOUCH_SCROLL){
                        state=RELEASE;
                        refreshByState();
                    }
                    break;
                case RELEASE:
                    toPadding(toPadding);
                    if(distance<(headerHight+150)){
                        state=PULL;
                        refreshByState();
                    }
                    break;
            }
    
    
        }
    

    refreshByState就是我们在滑动的时候根据state的状态来更新header的高度和内容信息
    (ps:这里我用的是动画将图片旋转了,小伙伴也可以用两张图片,一张下拉,一张上拉
    还有控件的获取要在解析header布局文件后获取)

     /*
        *通过当前state状态改变header的显示布局
         */
        private void refreshByState() {
    
            switch (state){
                case NONE:
                   // imageView.clearAnimation();
                    toPadding(-headerHight);
                    imageView.setVisibility(View.VISIBLE);
                    progressBar.setVisibility(View.GONE);
                    break;
                case PULL:
                    imageView.clearAnimation();
                    imageView.setVisibility(View.VISIBLE);
                    progressBar.setVisibility(View.GONE);
                    imageView.setAnimation(anim2);
                    tip.setText("下拉可刷新");
                    break;
                case RELEASE:
                    imageView.clearAnimation();
                    imageView.setVisibility(View.VISIBLE);
                    progressBar.setVisibility(View.GONE);
                    imageView.setAnimation(anim1);
                    tip.setText("松开可刷新");
                    break;
                case REFRESHING:
                    toPadding(50);
                    imageView.clearAnimation();
                    imageView.setVisibility(View.GONE);
                    progressBar.setVisibility(View.VISIBLE);
                    tip.setText("正在刷新");
                    break;
            }
        }
    

    7. 到这里我们就实现了监听下拉刷新了,现在来做刷新后的事情
    (ps:上面部分肯比较多,慢慢消化,乱的话可以看看源码)
    我们写一个刷新后把headr布局,各种信息还原并且更新时间的方法

     /*
        *刷新完成后调用此方法重新设置参数,并设置上次刷新时间
         */
        public void refreshComplete(){
            state=NONE;
            isRemak=false;
    
            refreshByState();
    
            Date date=new Date(System.currentTimeMillis());
    
            SimpleDateFormat format=new SimpleDateFormat("yy年mm月dd日 HH:MM:SS");
    
            String time=format.format(date);
    
            this.time.setText(time);
    
        }
    

    8. 最后一部,我们预留一个接口,让外面来完成刷新后的事情

     public class MyListView extends ListView implements AbsListView.OnScrollListener{
    ........
    ......
    .....
      public void setInterface(IRefreshen iRefreshen){
            this.iRefreshen=iRefreshen;
        }
    
        public interface IRefreshen{
            public  void onRefresh();
        }
    }
    
    

    主方法去实现该接口
    (PS: //这里使用handler延迟2秒来模拟刷新时候的等待)

    public class MainActivity extends AppCompatActivity implements MyListView.IRefreshen{
    
    
       private void findView() {
              ......
            myListView.setInterface(this);   
        }
       @Override
        public void onRefresh() {
            Handler handler=new Handler();
       
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    Map<String,Object> map=new HashMap<String,Object>();
                    map.put("text","刷新");
                    map.put("image",R.mipmap.ic_launcher);
                    data.add(0,map);
    
                    myListView.setAdapter(simpleAdapter);
                    simpleAdapter.notifyDataSetChanged();
    
                    myListView.refreshComplete();
    
                }
            },2000);
        }
    

    至此,恭喜大家实现下拉刷新了

    我来总结下思路:

    1. 创建自定义Listview布局,在主布局中使用该布局
    2. 创建header布局并添加进Listview的头部,并用paddingTop来隐藏
    3. 触摸事件的监听和处理
    4. 用回调接口,实现在MainAcitivity中刷新内容

    其实总体思路很简单,难点就在于触摸的时候对位置的监听和处理,这里比较繁琐,但是慢慢理解就不会觉得很难了

    这是我第一次分享,希望大家支持!!!
    有发现问题的可以留言,谢谢大家观赏,你的点赞是我继续分享的动力!!!

    项目github地址:https://github.com/DongDian455/Android.git

    相关文章

      网友评论

        本文标题:android案例---下拉刷新

        本文链接:https://www.haomeiwen.com/subject/nnpfrxtx.html