Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android自定義控件之仿汽車之家下拉刷新

Android自定義控件之仿汽車之家下拉刷新

編輯:關於Android編程


關於下拉刷新的實現原理我在上篇文章Android自定義控件之仿美團下拉刷新中已經詳細介紹過了,這篇文章主要介紹表盤的動畫實現原理

汽車之家的下拉刷新分為三個狀態:
第一個狀態為下拉刷新狀態(pull to refresh),在這個狀態下是一個表盤隨著下拉的距離動態改變指針的角度
第二個狀態為放開刷新狀態(release to refresh),在這個狀態下是指針角度變化的一個動畫

第一個狀態的實現:


這個效果我們使用自定義View來實現,我們從汽車之家apk中拿到下拉刷新所用到的兩張圖片:


我們將第一張圖片畫在畫布上作為背景,接著我們根據當前進度值來動態旋轉畫布,然後再將第二章圖片畫在畫布上,我們看到表針的旋轉實則是畫布在旋轉。

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
            //現將第一張圖片畫在畫布上
            canvas.drawBitmap(finalBackGroundBitmap,0,0,null);
            //旋轉畫布
            canvas.rotate(mCurrentProgress*2.7f,x/2,y/2);
            //將第二張圖片畫在旋轉後的畫布上
            canvas.drawBitmap(finalPointerBitmap, 0, 0, null);
    }

自定義View的完整代碼如下:

/**
 * Created by zhangqi on 15/10/17.
 */
public class AutoHome extends View{
    private Bitmap backGroundBitmap;
    public Bitmap pointerBitmap;
    private int x;
    private int y;
    private Bitmap finalBackGroundBitmap;
    private Bitmap finalPointerBitmap;
    private float mCurrentProgress;

    public AutoHome(Context context) {
        super(context);
        init(context);
    }


    public AutoHome(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public AutoHome(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }


    private void init(Context context) {
        backGroundBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.load_icon_dial2x));
        pointerBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.load_icon_pointer2x));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureWidth(heightMeasureSpec));
        x = getMeasuredWidth();
        y = getMeasuredHeight();
        finalBackGroundBitmap = Bitmap.createScaledBitmap(backGroundBitmap, x, y, false);
        finalPointerBitmap = Bitmap.createScaledBitmap(pointerBitmap, x, y, false);
    }
    private int measureWidth(int widMeasureSpec){
        int result = 0;
        int size = MeasureSpec.getSize(widMeasureSpec);
        int mode = MeasureSpec.getMode(widMeasureSpec);
        if (mode == MeasureSpec.EXACTLY){
            result = size;
        }else{
            result = backGroundBitmap.getWidth();
            if (mode == MeasureSpec.AT_MOST){
                result = Math.min(result,size);
            }
        }
        return result;

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

            canvas.drawBitmap(finalBackGroundBitmap,0,0,null);
            canvas.rotate(mCurrentProgress*2.7f,x/2,y/2);
            canvas.drawBitmap(finalPointerBitmap, 0, 0, null);
    }


    public void setCurrentProgress(float progress){
        mCurrentProgress = progress*100;
    }
}

接著我們在Activity中用SeekBar來模擬一個進度值,從而傳遞給我們自定義View

public class MainActivity extends AppCompatActivity {
    private SeekBar mSeekBar;
    private AutoHome mAutoHome;
    private float mCurrentProgress;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSeekBar = (SeekBar) findViewById(R.id.seekbar);
        mAutoHome = (AutoHome) findViewById(R.id.autohome);

        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                mCurrentProgress = (float)seekBar.getProgress()/(float)seekBar.getMax();
                mAutoHome.setCurrentProgress(mCurrentProgress);
                mAutoHome.invalidate();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }

第二個狀態的實現:

第二個狀態是表針在執行一個旋轉的動畫,我們可以將表針寫成一個自定義View,然後表盤作為背景圖片,然後表針View來執行rotate動畫即可

/**
 * Created by zhangqi on 15/10/27.
 */
public class PointerView extends View {
    private int x;
    private int y;
    private Bitmap finalPointerBitmap;
    private Bitmap pointerBitmap;
    public PointerView(Context context) {
        super(context);
        init();
    }

    public PointerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PointerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        pointerBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.load_icon_pointer2x));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureWidth(heightMeasureSpec));
        x = getMeasuredWidth();
        y = getMeasuredHeight();
        finalPointerBitmap = Bitmap.createScaledBitmap(pointerBitmap, x, y, false);
    }
    private int measureWidth(int widMeasureSpec){
        int result = 0;
        int size = MeasureSpec.getSize(widMeasureSpec);
        int mode = MeasureSpec.getMode(widMeasureSpec);
        if (mode == MeasureSpec.EXACTLY){
            result = size;
        }else{
            result = pointerBitmap.getWidth();
            if (mode == MeasureSpec.AT_MOST){
                result = Math.min(result,size);
            }
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //目的是讓表針初始位置為270度!
        canvas.rotate(270,x/2,y/2);
        canvas.drawBitmap(finalPointerBitmap,0,0,null);
    }
}

然後我們在xml文件中這樣寫:

 <framelayout android:id="@+id/anim_container" android:layout_height="45dp" android:layout_margin="15dp" android:layout_width="45dp" android:visibility="gone">

    

        
    </framelayout>

這樣就將表盤作為背景了,我們可以操作表針來執行rotate動畫



mAutoHomeAnim = (PointerView) headerView.findViewById(R.id.anim_pointer);
animation = AnimationUtils.loadAnimation(context, R.anim.pointer_rotate);
//執行動畫
mAutoHomeAnim.startAnimation(animation);

在listview中

由於下拉刷新核心代碼和美團下拉刷新是一樣的,這裡我只截取不一樣的部分

private void changeHeaderByState(int state){
        switch (state) {
        case DONE:
            headerView.setPadding(0, -headerViewHeight, 0, 0);
            //第一狀態的view顯示出來
            mAutoHome.setVisibility(View.VISIBLE);
            //先停止一下第二階段view的動畫
            mAutoHomeAnim.clearAnimation();
            //將第二階段view隱藏起來
            mAnimContainer.setVisibility(View.GONE);
            break;
        case RELEASE_TO_REFRESH:
            tv_pull_to_refresh.setText(放開刷新);

            break;
        case PULL_TO_REFRESH:
            tv_pull_to_refresh.setText(下拉刷新);
            //第一狀態view顯示出來
            mAutoHome.setVisibility(View.VISIBLE);
            //停止第二階段動畫
            mAutoHomeAnim.clearAnimation();
            //將第二階段view隱藏
            mAnimContainer.setVisibility(View.GONE);
            break;
        case REFRESHING:
            tv_pull_to_refresh.setText(正在刷新);
            //將第一階段view隱藏
            mAutoHome.setVisibility(View.GONE);
            //將第二階段view顯示出來
            mAnimContainer.setVisibility(View.VISIBLE);
            //先停止第二階段動畫
            mAutoHomeAnim.clearAnimation();
            //啟動第二階段動畫
            mAutoHomeAnim.startAnimation(animation);
            break;
        default:
            break;
        }
    }

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved