編輯:關於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);
由於下拉刷新核心代碼和美團下拉刷新是一樣的,這裡我只截取不一樣的部分
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;
}
}
在Ubuntu Server14.04上編譯Android6.0源碼,ubuntu編譯安卓源碼 此前編譯過Android4.4的源碼,但是現在Android都到了7.0
Android中訪問sdcard路徑的幾種方式,androidsdcard以前的Android(4.1之前的版本)中,SDcard路徑通過“/sdcard”或者“/mnt
初探ListView,初探網ListView可能是Android開發中最常用的一個控件,但要用的純熟還需要不斷的鍛煉。 建立簡單的ListView 1.在布局文件(.
官方翻譯不當導致的PowerShell運行失敗一例今天我在PowerShell.com看到一篇文章,是關於Gridview的應用,覺得挺好,決定自己嘗試一下。但是實際操作