Android仿知乎日报开屏页效果
先看看知乎日报开屏页的效果,非常漂亮的开屏效果:

然后我来一个:

也不错感觉可以以假乱真了,很简单,直接开始。
实现这个效果先制定个三步走策略:
底部布局上滑展示。
画一个知弧。
显示图片
底部布局上滑展示
直接上代码吧,属性动画基本使用
private void startAnimation() {//位移动画,从底部滑出,Y方向移动,mHeight是底部布局的高度ObjectAnimator translationAnimator= ObjectAnimator.ofFloat(rv_bottom, "translationY", mHeight, 0f);//设置时长translationAnimator.setDuration(1000);//透明度渐变动画ObjectAnimator alphaAnimatorator = ObjectAnimator.ofFloat(rv_bottom, "alpha", 0f,1f);//设置时长alphaAnimatorator.setDuration(2500);//添加监听器,位移结束后,画圆弧开始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();//两个动画一起执行set.play(translationAnimator).with(alphaAnimatorator);//goset.start();}
在位移动画结束的时候,调用了自定义的view的方法,开始了画弧的操作。
画个知弧
接下来开始画画~ 自定义一个view,重写ondraw方法,开画之前先初始化一个合适的画笔。
private void initPaint() {mPaint1 = new Paint();//设置画笔颜色mPaint1.setColor(Color.WHITE);// 设置画笔的样式为圆形mPaint1.setStrokeCap(Paint.Cap.ROUND);// 设置画笔的填充样式为描边mPaint1.setStyle(Paint.Style.STROKE);//抗锯齿mPaint1.setAntiAlias(true);//设置画笔宽度mPaint1.setStrokeWidth(mBorderWidth1);mPaint2 = new Paint();mPaint2.setColor(Color.WHITE);mPaint2.setStyle(Paint.Style.STROKE);mPaint2.setAntiAlias(true);mPaint2.setStrokeWidth(mBorderWidth2);}
mPaint1用来画弧,设置填充样式为描边,这样起码我们就能轻松画一个圆环了。其实要画的知弧就是一个圆环被啃去了一块的感觉~ 但被啃的地方很光滑,所以需要一个圆头的画笔 。
mPaint2用来画外面的圆角矩形环,设置也差不多。
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawColor(Color.BLACK);//矩形轮廓,圆弧在内部,给予一定的内边距RectF rectF1 = new RectF(mBorderWidth1/2+dipToPx(8), mBorderWidth1/2+dipToPx(8), getWidth() -mBorderWidth1/2-dipToPx(8),getWidth()-mBorderWidth1/2-dipToPx(8) );//画圆弧 参数1:矩形轮廓 参数2:起始位置 参数3:扫过的范围,初始为0 参数4:是否连接圆心canvas.drawArc(rectF1, 90, mCurrentRadian, false, mPaint1);//矩形轮廓RectF rectF2 = new RectF(mBorderWidth2/2,mBorderWidth2/2,getWidth()-mBorderWidth2/2,getWidth()-mBorderWidth2/2);//画圆角矩形边框 参数2 3设置x,y方向的圆角corner 都要设置canvas.drawRoundRect(rectF2,dipToPx(8),dipToPx(8),mPaint2);}
代码量很少,但要明确环的画法,不论是画圆环还是圆角矩形环,需要先确定一个基准矩形。基准矩形的位置和大小确定环的位置和大小。画弧的方法canvas.drawArc中的参数2 3设置了开始画弧的位置和画弧的范围。看一下运行效果,圆弧的起始画点在圆心的正下方,X轴正方向为0度,所以起始画点为90度。
接下来就使用不断增大画弧的范围的方式来完成动画的实现。上代码
private void startAnimationDraw() {//圆弧扫过范围为270度ValueAnimator valueAnimator=new ValueAnimator().ofFloat(0,270);//动画持续时间valueAnimator.setDuration(mDuration);//设置插值器,中间快两头慢valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());//添加状态监听器valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {//不断增大圆弧扫过的范围,并重绘来实现动画效果mCurrentRadian= (float) animation.getAnimatedValue();invalidate();}});valueAnimator.start();}
使用ValueAnimator.ofFloat创建一个值为0-270的动画,添加状态监听,在动画执行的过程中不断增大扫过的范围并重绘视图从而实现了画弧的动画效果。
整个过程就是canvas配合属性动画的方式完成了动态绘图,一点也不复杂。
显示图片
这里我使用的是Glide加载的本地图片,设置了延迟加载把握图片加载时机,获得更好的开屏效果
//延时加载图片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);
这里个人认为知乎也是用某种方式预先把图片下载到本地完成来把握精确地加载时机,不知道是不是这样。。
最后贴一下代码
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();//开始动画startAnimation();//延时加载图片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() {//位移动画,从底部滑出,Y方向移动ObjectAnimator translationAnimator= ObjectAnimator.ofFloat(rv_bottom, "translationY", mHeight, 0f);//设置时长translationAnimator.setDuration(1000);//透明度渐变动画ObjectAnimator alphaAnimatorator = ObjectAnimator.ofFloat(rv_bottom, "alpha", 0f,1f);//设置时长alphaAnimatorator.setDuration(2500);//添加监听器,位移结束后,画圆弧开始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();//两个动画一起执行set.play(translationAnimator).with(alphaAnimatorator);//goset.start();}}
自定义view
public class Zhview extends View {private Paint mPaint1; //圆弧画笔private Paint mPaint2; //外框画笔//圆弧宽度private int mBorderWidth1=dipToPx(5);//外框宽度private int mBorderWidth2=dipToPx(1.5f);//扫过的范围private float mCurrentRadian=0;//动画持续时长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();//设置画笔颜色mPaint1.setColor(Color.WHITE);// 设置画笔的样式为圆形mPaint1.setStrokeCap(Paint.Cap.ROUND);// 设置画笔的填充样式为描边mPaint1.setStyle(Paint.Style.STROKE);//抗锯齿mPaint1.setAntiAlias(true);//设置画笔宽度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);//矩形轮廓,圆弧在内部,给予一定的内边距RectF rectF1 = new RectF(mBorderWidth1/2+dipToPx(8), mBorderWidth1/2+dipToPx(8), getWidth() -mBorderWidth1/2-dipToPx(8),getWidth()-mBorderWidth1/2-dipToPx(8) );//画圆弧 参数1:矩形轮廓 参数2:起始位置 参数3:扫过的范围,初始为0 参数4:是否连接圆心canvas.drawArc(rectF1, 90, mCurrentRadian, false, mPaint1);//矩形轮廓RectF rectF2 = new RectF(mBorderWidth2/2,mBorderWidth2/2,getWidth()-mBorderWidth2/2,getWidth()-mBorderWidth2/2);//画圆角矩形边框 参数2 3设置x,y方向的圆角corner 都要设置canvas.drawRoundRect(rectF2,dipToPx(8),dipToPx(8),mPaint2);}private void startAnimationDraw() {//圆弧扫过范围为270度ValueAnimator valueAnimator=new ValueAnimator().ofFloat(0,270);//动画持续时间valueAnimator.setDuration(mDuration);//设置插值器,中间快两头慢valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());//添加状态监听器valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {//不断增大圆弧扫过的范围,并重绘来实现动画效果mCurrentRadian= (float) animation.getAnimatedValue();invalidate();}});valueAnimator.start();}//开始动画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="知乎日报"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
到这里就结束啦。
