Android仿知乎日报开屏页效果

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

ezgif.com-resize (2).gif

然后我来一个

ezgif.com-resize (1).gif

也不错~感觉可以以假乱真了~

很简单,直接开始。

实现这个效果先制定个三步走策略

  • 底部布局上滑展示。
  • 画一个知弧。
  • 显示图片

底部布局上滑展示

直接上代码吧,属性动画基本使用

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() {
      @Override
      public void onAnimationStart(Animator animation) {

      }

      @Override
      public void onAnimationEnd(Animator animation) {
        zhview.startAnimation();
      }

      @Override
      public void onAnimationCancel(Animator animation) {

      }

      @Override
      public void onAnimationRepeat(Animator animation) {

      }
    });
    AnimatorSet set = new AnimatorSet();
    //两个动画一起执行
    set.play(translationAnimator).with(alphaAnimatorator);
    //go
    set.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用来画外面的圆角矩形环,设置也差不多。

@Override
  protected 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() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        //不断增大圆弧扫过的范围,并重绘来实现动画效果
        mCurrentRadian= (float) animation.getAnimatedValue();
        invalidate();
      }
    });
    valueAnimator.start();
  }

使用ValueAnimator.ofFloat创建一个值为0-270的动画,添加状态监听,在动画执行的过程中不断增大扫过的范围并重绘视图从而实现了画弧的动画效果。

整个过程就是canvas配合属性动画的方式完成了动态绘图,一点也不复杂。

显示图片

这里我使用的是Glide加载的本地图片,设置了延迟加载把握图片加载时机,获得更好的开屏效果

 //延时加载图片
        new Handler().postDelayed(new Runnable() {
          @Override
          public 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;

  @Override
  protected 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() {
      @Override
      public void run() {
        //获得底部的高度
        mHeight=rv_bottom.getHeight();
        //开始动画
        startAnimation();
        //延时加载图片
        new Handler().postDelayed(new Runnable() {
          @Override
          public 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() {
      @Override
      public void onAnimationStart(Animator animation) {

      }

      @Override
      public void onAnimationEnd(Animator animation) {
        zhview.startAnimation();
      }

      @Override
      public void onAnimationCancel(Animator animation) {

      }

      @Override
      public void onAnimationRepeat(Animator animation) {

      }
    });
    AnimatorSet set = new AnimatorSet();
    //两个动画一起执行
    set.play(translationAnimator).with(alphaAnimatorator);
    //go
    set.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);
  }

  @Override
  protected 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() {
      @Override
      public 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));
  }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<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">

  <ImageView
    android:id="@+id/image"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@+id/rv_bottom" />

  <RelativeLayout
    android:id="@+id/rv_bottom"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:padding="20dp">

    <com.zhview.Zhview
      android:id="@+id/zhview"
      android:layout_width="46dp"
      android:layout_height="46dp"
      android:layout_marginLeft="10dp" />

    <TextView
      android: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"

      />

    <TextView
      android: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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android开屏页倒计时功能实现的详细教程

    最近我司产品提出了一个很常见的需求:App 在开屏页(Splash 界面) 需要加上一个 3s 倒计时按钮,可以选择看 3s 的广告,或者点击按钮跳过广告. 一.布局实现(使用 FrameLayout 悬浮在广告的右上角,显示倒计时的 TextView 的宽高尽量不要写死,要考虑字体很多的情况!!) <FrameLayout android:id="@+id/start_skip" android:layout_width="wrap_content" and

  • Material Design系列之Behavior实现Android知乎首页

    本博客目的:仿知乎首页向上滑动时动画隐藏Toolbar.FlocationActionButton.Tab导航,下滑时显示,如果和你的期望不同,那么你可以不需要看了,免的浪费你的宝贵时间噢. 效果预览 知乎效果: 本博客实现效果: 今天效果的源代码下载链接在文章末尾. 实现分析 这个效果其实并不难实现,但是它的用处很大,当用户手指上滑,屏幕上显示下方内容的时候,隐藏Toolbar.Tab导航.FAB来腾出更大的空间显示内容,让用户爽.简单粗暴,但这就是我们的目的. 首先就是头部的Toolbar,

  • Android模仿知乎的回答详情页的动画效果

    废话不多说,咱们第一篇文章就是模仿"知乎"的回答详情页的动画效果,先上个原版的效果图,咱们就是要做出这个效果 在实现之前,我们先根据上面的动画效果,研究下需求,因为gif帧数有限,所以不是很连贯,推荐你直接下载一个知乎,找到这个界面自己玩玩 ☞当文章往上移动到一定位置之后,最上面的标题栏Bar和问题布局Title是会隐藏的,回答者Author布局不会隐藏 ☞当文章往下移动移动到一定位置之后,原先隐藏的标题栏Bar和问题布局Title会下降显示 ☞当文章往上移动的时候,下部隐藏的Tool

  • Android仿知乎日报开屏页效果

    先看看知乎日报开屏页的效果,非常漂亮的开屏效果 ezgif.com-resize (2).gif 然后我来一个 ezgif.com-resize (1).gif 也不错~感觉可以以假乱真了~ 很简单,直接开始. 实现这个效果先制定个三步走策略 底部布局上滑展示. 画一个知弧. 显示图片 底部布局上滑展示 直接上代码吧,属性动画基本使用 private void startAnimation() { //位移动画,从底部滑出,Y方向移动,mHeight是底部布局的高度 ObjectAnimator

  • Android仿淘宝商品详情页效果

    本文实例为大家分享了Android仿淘宝商品详情页的具体代码,供大家参考,具体内容如下 Demo地址:先上效果图 效果就是上面图片的效果 接下来看看如何实现 首先我们来看下布局文件 <LinearLayout android:id="@+id/header" android:layout_width="match_parent" android:layout_height="72dp" android:paddingTop="24

  • Android仿知乎悬浮功能按钮FloatingActionButton效果

    前段时间在看属性动画,恰巧这个按钮的效果可以用属性动画实现,所以就来实践实践.效果基本出来了,大家可以自己去完善. 首先看一下效果图: 我们看到点击FloatingActionButton后会展开一些item,然后会有一个蒙板效果,这都是这个View的功能.那么这整个View肯定是个ViewGroup,我们一部分一部分来看. 首先是这个最小的Tag: 这个Tag带文字,可以是一个TextView,但为了美观,我们使用CardView,CardView是一个FrameLayout,我们要让它具有显

  • Android仿硬币转动微信红包动画效果

    项目需要研究了一下微信红包动画,即硬币转动的效果,原理其实就是三张不同角度的图片利用AnimationDrawable帧动画进行播放,在参考了案例之后,给自己记录一下完成的过程. 1,在XML文件中定义动画: 步骤如下: ①新建 Android 项目 ②在drawable目录中新建一个anim.xml(注意文件名小写) <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:andr

  • 基于jQuery和Bootstrap框架实现仿知乎前端动态列表效果

    最近基于jQuery和Bootstrap框架实现了一个仿知乎动态列表的前端效果,基本实现了和知乎动态列表相同的效果.如下: 1.基本列表项 2.列表项全文展开.折叠(图中为展开第一项) 3.评论项展开 4.列表数据加载(加载全部) 5.带动画效果的点赞功能 6.回复列表的hover显示功能 HTML代码如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta h

  • Android仿知乎客户端关注和取消关注的按钮点击特效实现思路详解

    先说明一下,项目代码已上传至github,不想看长篇大论的也可以先去下代码,对照代码,哪里不懂点哪里. 代码在这https://github.com/zgzczzw/ZHFollowButton 前几天发现知乎关注的点击效果确实赞,查了一下实现方式,刚好看到这个问题,花了一天时间终于把这个效果实现了,现在来回答一下,很不幸,楼上各位的答案都不全对,且听我一一道来. 首先,我先详细观察了一些知乎的效果,其中有一个很神奇的地方,如图: 注意看第二张图,这个圆形在扩散的时候,圆形底下的字还在,而且新的

  • Android利用悬浮按钮实现翻页效果

    今天给大家分享下自己用悬浮按钮点击实现翻页效果的例子. 首先,一个按钮要实现悬浮,就要用到系统顶级窗口相关的WindowManager,WindowManager.LayoutParams.那么在AndroidManifest.xml中添加权限: <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> 然后,我们要对WindowManager,WindowManager.Layout

  • Android仿支付宝的头部伸缩动画效果

    Android5.0推出的MaterialDesign库包含了处理头部工具栏的多个控件,不但允许自定义顶部导航栏,而且导航栏高度是可以伸缩的.如此一来,一方面导航栏能够放得下更多控件,另一方面在用户想看具体内容时也能腾出更多的屏幕空间. 这么说可能比较抽象,那就先来看看两张导航栏的效果图,第一张是导航栏完全展开时的界面,此时页面头部的导航栏占据了较大部分的高度: 第二张是导航栏完全收缩时的界面,此时头部导航栏只剩矮矮的一个长条. 看起来很眼熟是不是,上面的截图正是仿支付宝首页的头部效果.如果你熟

  • Android 仿今日头条简单的刷新效果实例代码

    点击按钮,先自动进行下拉刷新,也可以手动刷新,刷新完后,最后就多一行数据.有四个选项卡. 前两天导师要求做一个给本科学生预定机房座位的app,出发点来自这里.做着做着遇到很多问题,都解决了.这个效果感觉还不错,整理一下. MainActivity package com.example.fragmentmytest; import android.content.DialogInterface; import android.graphics.Color; import android.os.B

  • Android使用BottomTabBar实现底部导航页效果

    1. 导依赖 compile 'com.hjm:BottomTabBar:1.1.1' 2. 在所实现的XML中定义一下该控件 <com.hjm.bottomtabbar.BottomTabBar android:id="@+id/bottom_tab_bar" android:layout_width="match_parent" android:layout_height="match_parent" > </com.hjm

随机推荐