1. 程式人生 > >Android高仿QQ下拉重新整理

Android高仿QQ下拉重新整理

此次牽扯到的知識點有:Android手勢,Handler,java多執行緒,java聚合,Android幀動畫,屬性動畫;

如果有對上述提到過的知識點不太瞭解,或者程式設計能力較差的小夥伴可以關閉此頁面啦敲打一,因為接下來的裝逼過程

你可能會是一臉懵B委屈 。如果你執意要看也沒事啦,因為程式碼裡面我寫了足夠詳細的註釋,如果你努力用心去看的話

,我相信你還是能看懂滴!
                                       

本原始碼已經上傳至GitHub,有興趣的小夥伴歡迎歡迎對本外掛進行改進和升級

Github原始碼下載地址

都讓開,我要開始裝逼了奮鬥奮鬥奮鬥

得意裝逼圖:

由於程式碼裡面的註釋寫的非常詳細,我就只簡單的描述一下步驟吧!


1、我們先建立一個下拉出來的佈局xml檔案;檔名:listview_head_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#323232"
    android:layout_height="match_parent">

    <RelativeLayout
        android:id="@+id/pullLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="10dp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginTop="10dp">

            <RelativeLayout
                android:id="@+id/refresh"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true">

                <ImageView
                    android:id="@+id/progress_bar"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="10dp"
                    android:background="@drawable/progress_bar" />


                <TextView
                    android:id="@+id/progress_Text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true"
                    android:layout_toEndOf="@id/progress_bar"
                    android:text="提示文字......"
                    android:textColor="#fff" />

            </RelativeLayout>


            <ImageView
                android:id="@+id/refreshIcon"
                android:layout_width="20dp"
                android:layout_height="40dp"
                android:layout_centerVertical="true"
                android:layout_marginLeft="40dp"
                android:background="@drawable/rjd_down"
                android:paddingTop="-10dp" />

            <TextView
                android:id="@+id/pullText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:textColor="#fff" />
        </RelativeLayout>
    </RelativeLayout>
</RelativeLayout>


2、然後定義相關的動畫xml;
圓形幀動畫滾動條:progress.xml
這裡圖片大家就自己去找吧,百度上一大把的

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/toutiao__loading_12" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_11" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_10" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_09" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_08" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_07" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_06" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_05" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_04" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_03" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_02" android:duration="100"/>
    <item android:drawable="@drawable/toutiao__loading_01" android:duration="100"/>
</animation-list>


下拉箭頭旋轉動畫:
  pullimage.xml(箭頭向上旋轉)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:duration="200"
        android:fromDegrees="0"
        android:toDegrees="180"
        android:pivotX="50%"
        android:pivotY="50%"
        />
</set>

pullimage.xml(箭頭向下旋轉)    
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:duration="200"
        android:fromDegrees="180"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        />
</set>


3、建立一個CustomListView類,繼承ListView(詳細實現思路請看程式碼);

package com.example.zking.example;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;


/**
 * Created by Administrator on 2017/7/25 0025.
 */
public class CustomListView extends ListView {
    //記錄重新整理狀態  1 重新整理前、2 重新整理中、3重新整理成功
    private static int refreshState=1;
    //重新整理監聽事件介面
    private OnRefreshListener listener=null;
    //重新整理動畫
    private AnimationDrawable animDra;
    private Context context;
    //下拉的HeaderView
    private View headView;
    //HeaderView的高度
    private int headViewHeight;
    //下拉提示語
    private TextView pullText;
    private TextView progress_Text;
    //重新整理圖示
    private ImageView pullImage;
    private ImageView progress_bar;
    //控制高度的佈局
    private RelativeLayout pullLayout;
    //佈局管理器
    private ViewGroup.LayoutParams lp;

    //標記標籤
    //記錄是否第一次改變高度
    boolean firstFlag=true;
    //記錄改變下拉圖示的狀態
    boolean changePullImageFlag=true;
        //按下的Y軸 釋放的Y軸 下拉高度(按下-釋放)
    float flagYDown,flagYMove,flagY;

    //定義一個Handler來處理子執行緒給我們返回的資訊
    Handler myHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
             switch(msg.what){
                 case 1:
                     progress_Text.setText("重新整理成功!");
                     animDra.stop();
                     progress_bar.setBackgroundResource(R.drawable.obu);
                     break;
                 case 2:
                     //狀態設為 重新整理完畢
                     refreshState=3;
                     changeViewHeight(headViewHeight,0);
                     if(listener!=null){
                         listener.refreshAfter(CustomListView.this);
                     }
                     break;
             }
        }
    };


    public CustomListView(Context context) {
        super(context);
    }

    public CustomListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context=context;
        headView=(View)LayoutInflater.from(context).inflate(R.layout.listview_head_layout,null);
        pullLayout=(RelativeLayout) headView.findViewById(R.id.pullLayout);
        lp=pullLayout.getLayoutParams();
        headView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                //判斷是否是第一次改變佈局,如果是的話,則把佈局的高度設為0
                if(firstFlag){
                    headViewHeight=v.getHeight();
                    lp.height=0;
                    pullLayout.setLayoutParams(lp);
                    firstFlag=false;
                }
            }
        });
        //設定HeaderView
        this.addHeaderView(headView);
        initHeadView();
    }

    //初始化控制元件
    void initHeadView(){
        //圓形重新整理動畫
        animDra = (AnimationDrawable) headView.findViewById(R.id.progress_bar).getBackground();
        //圓形重新整理圖示
        progress_bar=(ImageView) headView.findViewById(R.id.progress_bar);
        //重新整理提示文字
        progress_Text=(TextView) headView.findViewById(R.id.progress_Text);
        //下滑的提示文字
        pullText=(TextView)headView.findViewById(R.id.pullText);
        //下滑箭頭圖示
        pullImage=(ImageView)headView.findViewById(R.id.refreshIcon);

    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //判斷是否是最頂部和是否處於重新整理狀態
        if(this.getFirstVisiblePosition()==0&&refreshState!=2){
        switch(ev.getAction()){
            case MotionEvent.ACTION_DOWN://手指按下時
                flagYDown=ev.getY();
                //按下時隱藏圓形進度條和提示文字,顯示下拉箭頭圖、提示文字
                progress_bar.setVisibility(View.GONE);
                progress_Text.setVisibility(View.GONE);
                pullText.setVisibility(View.VISIBLE);
                pullImage.setBackgroundResource(R.drawable.rjd_down);
                refreshState=1;
                if(listener!=null){
                    listener.refreshBefore(CustomListView.this);
                }
                break;
            case MotionEvent.ACTION_MOVE://手指滑動時
                this.setSelection(0);
                flagYMove=ev.getY();
                //下拉的高度
                flagY=(float)((flagYMove-flagYDown)*0.3);//這裡*0.3是設定下拉的難易度,*的數越大越容易下拉
                //判斷下拉的高度是否大於ListView頭佈局原始的高度
                if(flagY>headViewHeight){
                    pullText.setText("鬆開立即重新整理");
                    //設定上下小箭頭的旋轉動畫
                    //changePullImageFlag,設定一個標籤,防止多次設定動畫,當下拉的高寬改變時才設定動畫
                    if(changePullImageFlag){
                        changePullIconAnimation(1,pullImage);
                        changePullImageFlag=false;
                    }
                }else{
                    pullText.setText("下拉重新整理");
                    if(changePullImageFlag==false){
                        changePullIconAnimation(0,pullImage);
                        changePullImageFlag=true;
                    }
                }
                //判斷一下下拉的高度是否大於0
               if(flagY>0){
                       lp.height=(int)flagY;
                       pullLayout.setLayoutParams(lp);
                }
                break;
            case MotionEvent.ACTION_UP://手指放開時
                //判斷下拉的高度是否大於ListView頭佈局原始的高度
                if(flagY>headViewHeight){
                    pullText.setVisibility(View.GONE);
                    pullImage.setBackgroundResource(R.color.pullImage);
                    //將圓形進度條,和提示文字顯示出來
                    progress_bar.setVisibility(View.VISIBLE);
                    progress_Text.setVisibility(View.VISIBLE);
                    progress_Text.setText("正在重新整理...");
                    //呼叫改變高度的緩衝動畫的方法,並傳入引數
                    changeViewHeight(lp.height,headViewHeight);
                    progress_bar.setBackgroundResource(R.drawable.progress_bar);
                    animDra=(AnimationDrawable) progress_bar.getBackground();
                    animDra.start();
                    //將狀態設定為 正在重新整理
                    refreshState=2;
                    //呼叫正在重新整理
                    if(listener!=null){
                        listener.refreshStart(CustomListView.this);
                    }
                }else{
                        changeViewHeight(lp.height,0);
                }
                break;
        }
        }
        return super.dispatchTouchEvent(ev);
    }



    //改變高度的緩衝動畫           開始動畫的高度     結束動畫的高度
    public void changeViewHeight(int startHeight,int endHeight){
        ValueAnimator va;
        //判斷開始的高度和結束的高度是否大於30大於30就設定啟用彈跳效果
        if(startHeight>headViewHeight||startHeight<30){
         va= ValueAnimator.ofInt(startHeight,endHeight);
        }else{
                                  //開始高度  結束高度 彈跳的高度  結束高度
           va= ValueAnimator.ofInt(startHeight,endHeight,15,endHeight);
        }
        //監聽動畫改變的事件
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                //獲取當前的height值
                int h =(Integer)valueAnimator.getAnimatedValue();
                //動態更新高度
                lp.height=h;
                pullLayout.setLayoutParams(lp);
            }
        });
        va.setDuration(200);
        //開始動畫
        va.start();
    }

   //  改變下拉箭頭圖示動畫           狀態  0轉上去(鬆開立即重新整理) 1轉下來(下拉重新整理)
    public void changePullIconAnimation(int state,ImageView view){
        Animation anim;
        if(state==1){
            anim=AnimationUtils.loadAnimation(this.getContext(),R.anim.pullimage);
        }else{
            anim=AnimationUtils.loadAnimation(this.getContext(),R.anim.pullimage_down);
        }
        anim.setFillAfter(true);
        view.startAnimation(anim);
    }

    //設定重新整理監聽
    public void setOnRefreshListener(OnRefreshListener listener){
        this.listener=listener;
    }



}


4、建立一個介面用來作為我們CustomListView類的重新整理監聽介面;

package com.example.zking.example;
/**
 * Created by Administrator on 2017/7/29 0029.
 */
public interface OnRefreshListener {
    public void refreshBefore(CustomListView civ);//重新整理前
    public void refreshAfter(CustomListView civ);//重新整理後
    public void refreshStart(CustomListView civ);//開始重新整理
}

5、在我們展示的xml裡面設定我們自定義的CustomListView,使用方法和ListView一樣;

<?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:orientation="vertical"
    >
    <com.example.zking.example.CustomListView
        android:id="@+id/customLV"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </com.example.zking.example.CustomListView>
</LinearLayout>
6、在java類裡面進行找出我們定義的CustomListView,為它初始出一些資料,然後再設定監聽方法即可
package com.example.zking.example;

import android.graphics.drawable.AnimationDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    CustomListView clv;
    List<Object> list = new ArrayList<Object>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        clv=(CustomListView)findViewById(R.id.customLV);
        initDataSource();
        clv.setAdapter(new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,list));
        //設定重新整理監聽事件
        clv.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void refreshBefore(CustomListView civ) {
            }
            @Override
            public void refreshAfter(CustomListView civ) {
            }
            @Override
            public void refreshStart(final CustomListView civ) {
                //這裡new一個執行緒,用於模仿網路重新整理請求時長
                new Thread(){
                    @Override
                    public void run() {
                        try {
                            this.sleep(4000);
                            civ.myHandler.sendEmptyMessage(1);
                            this.sleep(1000);
                            clv.myHandler.sendEmptyMessage(2);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }.start();
            }
        });


    }
    //初始資料
    public void initDataSource() {
        for (int i = 0; i <30; i++) {
            list.add("a" + i);
        }
    }
}

呼呼,好啦   老夫已裝完逼。接下來大夥兒去消化消化吧。


如需原始碼和素材的小夥伴,可以加我的QQ2557606319,新增好友時驗證訊息記得填寫“CSDN”。