Android仿知乎日?qǐng)?bào)開屏頁效果
先看看知乎日?qǐng)?bào)開屏頁的效果,非常漂亮的開屏效果:

然后我來一個(gè):

也不錯(cuò)感覺可以以假亂真了,很簡單,直接開始。
實(shí)現(xiàn)這個(gè)效果先制定個(gè)三步走策略:
底部布局上滑展示。
畫一個(gè)知弧。
顯示圖片
底部布局上滑展示
直接上代碼吧,屬性動(dòng)畫基本使用
private void startAnimation() {//位移動(dòng)畫,從底部滑出,Y方向移動(dòng),mHeight是底部布局的高度ObjectAnimator translationAnimator= ObjectAnimator.ofFloat(rv_bottom, "translationY", mHeight, 0f);//設(shè)置時(shí)長translationAnimator.setDuration(1000);//透明度漸變動(dòng)畫ObjectAnimator alphaAnimatorator = ObjectAnimator.ofFloat(rv_bottom, "alpha", 0f,1f);//設(shè)置時(shí)長alphaAnimatorator.setDuration(2500);//添加監(jiān)聽器,位移結(jié)束后,畫圓弧開始translationAnimator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {zhview.startAnimation();}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});AnimatorSet set = new AnimatorSet();//兩個(gè)動(dòng)畫一起執(zhí)行set.play(translationAnimator).with(alphaAnimatorator);//goset.start();}
在位移動(dòng)畫結(jié)束的時(shí)候,調(diào)用了自定義的view的方法,開始了畫弧的操作。
畫個(gè)知弧
接下來開始畫畫~ 自定義一個(gè)view,重寫ondraw方法,開畫之前先初始化一個(gè)合適的畫筆。
private void initPaint() {mPaint1 = new Paint();//設(shè)置畫筆顏色mPaint1.setColor(Color.WHITE);// 設(shè)置畫筆的樣式為圓形mPaint1.setStrokeCap(Paint.Cap.ROUND);// 設(shè)置畫筆的填充樣式為描邊mPaint1.setStyle(Paint.Style.STROKE);//抗鋸齒mPaint1.setAntiAlias(true);//設(shè)置畫筆寬度mPaint1.setStrokeWidth(mBorderWidth1);mPaint2 = new Paint();mPaint2.setColor(Color.WHITE);mPaint2.setStyle(Paint.Style.STROKE);mPaint2.setAntiAlias(true);mPaint2.setStrokeWidth(mBorderWidth2);}
mPaint1用來畫弧,設(shè)置填充樣式為描邊,這樣起碼我們就能輕松畫一個(gè)圓環(huán)了。其實(shí)要畫的知弧就是一個(gè)圓環(huán)被啃去了一塊的感覺~ 但被啃的地方很光滑,所以需要一個(gè)圓頭的畫筆 。
mPaint2用來畫外面的圓角矩形環(huán),設(shè)置也差不多。
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawColor(Color.BLACK);//矩形輪廓,圓弧在內(nèi)部,給予一定的內(nèi)邊距RectF rectF1 = new RectF(mBorderWidth1/2+dipToPx(8), mBorderWidth1/2+dipToPx(8), getWidth() -mBorderWidth1/2-dipToPx(8),getWidth()-mBorderWidth1/2-dipToPx(8) );//畫圓弧 參數(shù)1:矩形輪廓 參數(shù)2:起始位置 參數(shù)3:掃過的范圍,初始為0 參數(shù)4:是否連接圓心canvas.drawArc(rectF1, 90, mCurrentRadian, false, mPaint1);//矩形輪廓RectF rectF2 = new RectF(mBorderWidth2/2,mBorderWidth2/2,getWidth()-mBorderWidth2/2,getWidth()-mBorderWidth2/2);//畫圓角矩形邊框 參數(shù)2 3設(shè)置x,y方向的圓角corner 都要設(shè)置canvas.drawRoundRect(rectF2,dipToPx(8),dipToPx(8),mPaint2);}
代碼量很少,但要明確環(huán)的畫法,不論是畫圓環(huán)還是圓角矩形環(huán),需要先確定一個(gè)基準(zhǔn)矩形。基準(zhǔn)矩形的位置和大小確定環(huán)的位置和大小。畫弧的方法canvas.drawArc中的參數(shù)2 3設(shè)置了開始畫弧的位置和畫弧的范圍??匆幌逻\(yùn)行效果,圓弧的起始畫點(diǎn)在圓心的正下方,X軸正方向?yàn)?度,所以起始畫點(diǎn)為90度。
接下來就使用不斷增大畫弧的范圍的方式來完成動(dòng)畫的實(shí)現(xiàn)。上代碼
private void startAnimationDraw() {//圓弧掃過范圍為270度ValueAnimator valueAnimator=new ValueAnimator().ofFloat(0,270);//動(dòng)畫持續(xù)時(shí)間valueAnimator.setDuration(mDuration);//設(shè)置插值器,中間快兩頭慢valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());//添加狀態(tài)監(jiān)聽器valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {//不斷增大圓弧掃過的范圍,并重繪來實(shí)現(xiàn)動(dòng)畫效果mCurrentRadian= (float) animation.getAnimatedValue();invalidate();}});valueAnimator.start();}
使用ValueAnimator.ofFloat創(chuàng)建一個(gè)值為0-270的動(dòng)畫,添加狀態(tài)監(jiān)聽,在動(dòng)畫執(zhí)行的過程中不斷增大掃過的范圍并重繪視圖從而實(shí)現(xiàn)了畫弧的動(dòng)畫效果。
整個(gè)過程就是canvas配合屬性動(dòng)畫的方式完成了動(dòng)態(tài)繪圖,一點(diǎn)也不復(fù)雜。
顯示圖片
這里我使用的是Glide加載的本地圖片,設(shè)置了延遲加載把握?qǐng)D片加載時(shí)機(jī),獲得更好的開屏效果
//延時(shí)加載圖片new Handler().postDelayed(new Runnable() {@Overridepublic void run() {Glide.with(MainActivity.this).load(R.drawable.timg).centerCrop().skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.NONE).crossFade(500).into(image);}},2000);
這里個(gè)人認(rèn)為知乎也是用某種方式預(yù)先把圖片下載到本地完成來把握精確地加載時(shí)機(jī),不知道是不是這樣。。
最后貼一下代碼
activity
public class MainActivity extends AppCompatActivity {private RelativeLayout rv_bottom;private Zhview zhview;private float mHeight;private ImageView image;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);rv_bottom= (RelativeLayout) this.findViewById(R.id.rv_bottom);zhview= (Zhview) this.findViewById(R.id.zhview);image= (ImageView) this.findViewById(R.id.image);rv_bottom.post(new Runnable() {@Overridepublic void run() {//獲得底部的高度mHeight=rv_bottom.getHeight();//開始動(dòng)畫startAnimation();//延時(shí)加載圖片new Handler().postDelayed(new Runnable() {@Overridepublic void run() {Glide.with(MainActivity.this).load(R.drawable.timg).centerCrop().skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.NONE).crossFade(500).into(image);}},2000);}});}private void startAnimation() {//位移動(dòng)畫,從底部滑出,Y方向移動(dòng)ObjectAnimator translationAnimator= ObjectAnimator.ofFloat(rv_bottom, "translationY", mHeight, 0f);//設(shè)置時(shí)長translationAnimator.setDuration(1000);//透明度漸變動(dòng)畫ObjectAnimator alphaAnimatorator = ObjectAnimator.ofFloat(rv_bottom, "alpha", 0f,1f);//設(shè)置時(shí)長alphaAnimatorator.setDuration(2500);//添加監(jiān)聽器,位移結(jié)束后,畫圓弧開始translationAnimator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {zhview.startAnimation();}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});AnimatorSet set = new AnimatorSet();//兩個(gè)動(dòng)畫一起執(zhí)行set.play(translationAnimator).with(alphaAnimatorator);//goset.start();}}
自定義view
public class Zhview extends View {private Paint mPaint1; //圓弧畫筆private Paint mPaint2; //外框畫筆//圓弧寬度private int mBorderWidth1=dipToPx(5);//外框?qū)挾?/span>private int mBorderWidth2=dipToPx(1.5f);//掃過的范圍private float mCurrentRadian=0;//動(dòng)畫持續(xù)時(shí)長private int mDuration=1500;public Zhview(Context context) {this(context,null);}public Zhview(Context context, @Nullable AttributeSet attrs) {this(context, attrs,0);}public Zhview(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);//初始化畫筆initPaint();}private void initPaint() {mPaint1 = new Paint();//設(shè)置畫筆顏色mPaint1.setColor(Color.WHITE);// 設(shè)置畫筆的樣式為圓形mPaint1.setStrokeCap(Paint.Cap.ROUND);// 設(shè)置畫筆的填充樣式為描邊mPaint1.setStyle(Paint.Style.STROKE);//抗鋸齒mPaint1.setAntiAlias(true);//設(shè)置畫筆寬度mPaint1.setStrokeWidth(mBorderWidth1);mPaint2 = new Paint();mPaint2.setColor(Color.WHITE);mPaint2.setStyle(Paint.Style.STROKE);mPaint2.setAntiAlias(true);mPaint2.setStrokeWidth(mBorderWidth2);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawColor(Color.BLACK);//矩形輪廓,圓弧在內(nèi)部,給予一定的內(nèi)邊距RectF rectF1 = new RectF(mBorderWidth1/2+dipToPx(8), mBorderWidth1/2+dipToPx(8), getWidth() -mBorderWidth1/2-dipToPx(8),getWidth()-mBorderWidth1/2-dipToPx(8) );//畫圓弧 參數(shù)1:矩形輪廓 參數(shù)2:起始位置 參數(shù)3:掃過的范圍,初始為0 參數(shù)4:是否連接圓心canvas.drawArc(rectF1, 90, mCurrentRadian, false, mPaint1);//矩形輪廓RectF rectF2 = new RectF(mBorderWidth2/2,mBorderWidth2/2,getWidth()-mBorderWidth2/2,getWidth()-mBorderWidth2/2);//畫圓角矩形邊框 參數(shù)2 3設(shè)置x,y方向的圓角corner 都要設(shè)置canvas.drawRoundRect(rectF2,dipToPx(8),dipToPx(8),mPaint2);}private void startAnimationDraw() {//圓弧掃過范圍為270度ValueAnimator valueAnimator=new ValueAnimator().ofFloat(0,270);//動(dòng)畫持續(xù)時(shí)間valueAnimator.setDuration(mDuration);//設(shè)置插值器,中間快兩頭慢valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());//添加狀態(tài)監(jiān)聽器valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {//不斷增大圓弧掃過的范圍,并重繪來實(shí)現(xiàn)動(dòng)畫效果mCurrentRadian= (float) animation.getAnimatedValue();invalidate();}});valueAnimator.start();}//開始動(dòng)畫public void startAnimation(){startAnimationDraw();}private int dipToPx(float dip) {float density = getContext().getResources().getDisplayMetrics().density;return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));}}
布局文件
<RelativeLayout 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"android:background="@android:color/black"tools:context="com.zhview.MainActivity"><ImageViewandroid:id="@+id/image"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_above="@+id/rv_bottom" /><RelativeLayoutandroid:id="@+id/rv_bottom"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:padding="20dp"><com.zhview.Zhviewandroid:id="@+id/zhview"android:layout_width="46dp"android:layout_height="46dp"android:layout_marginLeft="10dp" /><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="20dp"android:layout_toRightOf="@+id/zhview"android:text="知乎日?qǐng)?bào)"android:textColor="@android:color/white"????????????android:textSize="19sp"?/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignBottom="@+id/zhview"android:layout_marginLeft="20dp"android:layout_toRightOf="@+id/zhview"android:text="每天三次,每次七分鐘"android:textColor="@android:color/darker_gray"android:textSize="13sp" />RelativeLayout>RelativeLayout>
源碼地址:
https://github.com/yanyiqun001/zhview
到這里就結(jié)束啦。
