zoukankan      html  css  js  c++  java
  • 高级UI晋升之常用View(三)上篇

    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680
    本篇文章将先从以下两个内容来介绍常用View:

    • [RecycleView]
    • [CardView]

    一、RecycleView

    1.1、RecycleView介绍

    官方介绍为:

    A flexible view for providing a limited window into a large data set。

    翻译过来就是:

    为大数据集提供的有限窗口的灵活视图。

    用官方的语言描述,总是不能让我们直接的理解他的含义;但是用过RecycleView的同学都知道,他是一个强大的控件,只需要简单配置,我们就很容易的实现之前ListView,GridView和瀑布流的效果;当然它不是简单的将这些大数据展示控件融为一体,他还可以比较灵活的转换这些控件的展示方向(横向展示,竖向展示等),还有item的删除和添加动画等等功能;当然在RecycleView的使用过程中,也有一下让人感觉不太爽的地方,比如:item的点击事件需要自己实现了(没有OnItemClickListener了),item的分割线也需要继承类来实现等等;

    接下来我们就一起学习一下RecycleView。

    1.2、RecycleView基本使用

    用过ListView的同学上手就比较容易了,初学者也没什么困难。

    基本就三步:1、绘制布局文件;2、绘制item文件3、在Activity中给RecycleView填充数据;

    来看看简单的样例:

    1、绘制布局文件layout_main.xml

    <?version="1.0" encoding="utf-8"?>
    
    xmlns:android="http://schemas.android.com/apk/res/android"
    
    android:layout_width="match_parent"
    
    android:layout_height="match_parent">
    
      android:id="@+id/recyclerView"
    
      android:layout_width="match_parent"
    
      android:layout_height="wrap_content" />
    

    2、绘制layout_item.xml文件

    <?version="1.0" encoding="utf-8"?>
    
    xmlns:android="http://schemas.android.com/apk/res/android"
    
    android:layout_width="match_parent"
    
    android:layout_height="wrap_content">
    
      android:id="@+id/id_num"
    
      android:layout_width="match_parent"
    
      android:layout_height="50dp"
    
      android:gravity="center"
    
      android:text="1" />
    

    3、在Activity中给RecycleView填充数据

    public class MainActivity extends AppCompatActivity {
    
    private RecyclerView recyclerView;
    
    private List mDatas;
    
    private MainAdapter adapter;
    
    @Override
    
    protected
    
    void onCreate(Bundle savedInstanceState) {
    
      super.onCreate(savedInstanceState);
    
      setContentView(R.layout.activity_main);
    
      recyclerView = findViewById(R.id.recyclerView);
    
      recyclerView.setLayoutManager(new LinearLayoutManager(this));
    
      initData();
    
      adapter = new MainAdapter();
    
      recyclerView.setAdapter(adapter);
    
    }
    
    private void initData(){
    
      mDatas = new ArrayList<>();
    
      for(int i=0 ;i< 100;i++){
    
          mDatas.add("第"+i+"个item");
    
      }
    
    }
    
    class MainAdapter extends RecyclerView.Adapter{
    
      @Override
    
      public MyViewHolder onCreateViewHolder(ViewGroup parent, int
    
    viewType) {
    
          View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.layout_item,parent,false);
    
          MyViewHolder holder = new MyViewHolder(view);
    
          return holder;
    
      }
    
      @Override
    
      public void onBindViewHolder(MyViewHolder holder, int position) {
    
          holder.tv.setText(mDatas.get(position));
    
      }
    
      @Override
    
      public int getItemCount() {
    
          return mDatas.size();
    
      }
    
      public class MyViewHolder extends RecyclerView.ViewHolder {
    
          TextView tv;
    
          public MyViewHolder(View itemView) {
    
              super(itemView);
    
              tv = itemView.findViewById(R.id.id_num);
    
          }
    
      }
    
    }
    
    }
    

    在第三步中,我们可以看到:获取到RecycleView之后,给他设置了一个LayoutManager;这个LayoutManager就是

    用来设置RecycleView的数据展示样式,是ListView样式的,GridView样式的,还是瀑布流样式的,这个属性的可选项

    有LinearLayoutManager(线性布局),StaggeredGridLayoutManager(错落网格布局,瀑布流),GridLayoutManager

    (网格布局);

    1.3、RecycleView样式

    上面我们知道RecycleView的样式基本是由LayoutManager控制的,下面我们就学习一下这写LayoutManager基本使用。

    1、LinearLayoutManager

    第一种构造方法:

    new LinearLayoutManager(Context context)

    参数为上下文环境,实现的是默认的垂直布局。展示的样式和ListView一样。

    第二种构造方法:

    new LinearLayoutManager( Context context, int orientation, boolean reverseLayout)

    第一个参数为上下文环境;第二个参数为布局显示方式,表示列表横向滚动,还是竖向滚动(VERTICAL,

    HORIZONTAL);第三个参数为布尔值,表示展示的数据是否反转。

    2、StaggeredGridLayoutManager

    构造方法:new StaggeredGridLayoutManager(int spanCount,int orientation)

    使用错列的布局,指定两个参数,一个是要显示的列数spanCount,一个是显示的方向orientation;

    3、GridLayoutManager

    第一种构造方法:

    new GridLayoutManager(Context context, int spanCount)

    第一个参数为上下文环境,第二个显示列数,默认显示垂直布局

    第二种构造方法:

    new GridLayoutManager( Context context, int spanCount, int orientation,boolean reverseLayout)

    第一个参数为上下文环境,第二个显示列数,第三个参数为方向,第四个参数为是否反转

    以上的三种LayoutManager,大家可以尝试用一下,使用以后你会发现RecycleView原来这么强大。

    1.4、RecycleView item改变时的动画效果

    1、RecycleView item的默认动画效果

    RecyclerView有一个方法RecyclerView.setItemAnimator(),我们可以通过这个方法给item设置add和delete时的动画效果;查看源码,我们可以看到RecyclerView.itemAnimator是个抽象类,我们可以使用他子类的子类DefaultItemAnimator,来实现默认的动态效果;

    首先我们可以看一下DefaultItemAnimator的源码,我们可以看到有四个这样的方法:animateAdd(),animateMove(),

    animateDelete(),animateChange();那么,现在我们就可以写一个样例试一下默认动画;

    (1)首先在MainActivity中添加如下代码:

    DefaultItemAnimator defaultItemAnimator = new DefaultItemAnimator();
    
    defaultItemAnimator.setAddDuration(1000);
    
    defaultItemAnimator.setRemoveDuration(1000);
    
    recyclerView.setItemAnimator(defaultItemAnimator);
    

    (2)然后我们在adapter中添加删除item的方法,需要注意的是,当数据添加或者删除的的时候,不再是使用notifyDataChange()

    方法,而是每种操作都有自己的方法notifyItemInserted,notifyItemRemoved,notifyItemChanged,notifyItemMoved:

    /**
    
    * 增加数据*/
    
    public void addData(int position) {
    
      mDatas.add(position, "add");
    
      notifyItemInserted(position);  //注意这里
    
    }
    
    /*** 移除数据*/
    
    public void removeData(int position) {
    
    mDatas.remove(position);
    
    notifyItemRemoved(position);
    
      //注意这里
    
    }
    

    (3)最后我们在菜单中添加add,delete按钮:

      @Override
    
    public
    
    boolean onCreateOptionsMenu(Menu menu) {
    
      getMenuInflater().inflate(R.menu.menu,menu);
    
      return super.onCreateOptionsMenu(menu);
    
    }
    
    @Override
    
    public boolean onOptionsItemSelected(MenuItem item){
    
    switch(item.getItemId()){
    
    case R.id.action_add:
    
      adapter.addData(1);
    
    break;
    
    case R.id.action_remove:
    
      adapter.removeData(1);
    
      break;
    
    }
    
    return true;
    
    }
    
    menu布局文件:
    
    <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"
    
    tools:context="com.text.MainActivity">
    
    android:id="@+id/action_add"
    
    android:orderInCategory="1"
    
    android:title="add"
    
    app:showAsAction="ifRoom">
    
    android:id="@+id/action_remove"
    
    android:orderInCategory="2"
    
    android:title="delete"
    
    app:showAsAction="ifRoom"
    
    />
    

    这样我们轻轻松松就完成了RecycleView的四种动画;

    2、自定义item的动画效果

    上面我们很轻松的就实现了item的两种动画,我们也知道item的动画是由DefaultItemAnimator这个类定义的;

    那么我们是不是可以向DefaultItemAnimator一样继承他的父类,来实现自定义动画呢?显然可以;

    我我们可以参考DefaultItemAnimator,修改它的以下方法来实现动态效果:

    animateAdd()
    
    animateAddImpl()
    
    animateRemove()
    
    animateRemoveImpl()
    
    animateMove()
    
    animateMoveImpl()
    
    animateChange()
    
    animateChangeImpl()
    
    下面我们以添加item的动画为例,做一个从左面插入item的动画:
    
    @Override
    
    public boolean animateAdd(final RecyclerView.ViewHolder holder) {
    
    resetAnimation(holder);
    
    ViewCompat.setAlpha(holder.itemView,0);
    
    ViewCompat.setTranslationX(holder.itemView,-holder.itemView.getWidth());
    
    mPendingAdditions.add(holder);
    
    return true;
    
    }
    
    void animateAddImpl(final RecyclerView.ViewHolder holder) {
    
    final View view = holder.itemView;
    
    final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
    
    mAddAnimations.add(holder);
    
    animation.alpha(1).translationX(0).setDuration(getAddDuration).
    
    setListener(new VpaListenerAdapter() {
    
      @Override
    
      public void onAnimationStart(View view) {
    
          dispatchAddStarting(holder);
    
      }
    
      @Override
    
      public void onAnimationCancel(View view) {
    
          ViewCompat.setAlpha(view, 1);
    
      }
    
      @Override
    
      public void onAnimationEnd(View view) {
    
          animation.setListener(null);
    
          dispatchAddFinished(holder);
    
          mAddAnimations.remove(holder);
    
          dispatchFinishedWhenDone();
    
      }
    
    }).start();
    
    }
    

    以此为例,其他的动画效果就可以做出来了。

    1.5、RecycleView分割线的定义

    原来在listview中我们通过配置Android:divider属性很容易就可以为listview设置分割线,但在RecycleView中,没有了这个属性;

    我们只能通过addItemDecoration方法为它设置分割线,方法如下:

    recyclerView.addItemDecoration(new
    
    DividerItemDecoration(this,DividerItemDecoration.VERTICAL));
    

    当然我们也可以向修改动画一样,自定义分割线样式,如下:

    public class TestDividerItemDecoration extends RecyclerView.ItemDecoration {
    
    @Override
    
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,RecyclerView.State state) {
    
      super.getItemOffsets(outRect, view, parent, state);
    
      if (parent.getChildAdapterPosition(view) != 0){
    
          outRect.top = 1;
    
      }
    
    }
    
    }
    

    二、CardView使用

    Android 5.0 版本中新增了CardView,CardView继承自FrameLayout类,并且可以设置圆角和阴影,使得控件具有立体性,也可以包含其他的布局容器和控件。

    2.1.配置build.gradle

    如果SDK低于5.0,我们仍旧要引入v7包。在build.gradle 中加入如下代码已自动导入 support-v7包。记得配置完再重新Build一下工程。

    compile 'com.android.support:appcompat-v7:22.2.1‘
    compile 'com.android.support:cardview-v7:22.1.0'
    

    2.2.使用CardView实现如下效果:

     
    19956127-d1305d6cd732c53c.png
     

    布局如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:orientation="vertical">
        <android.support.v7.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="250dp"
            android:id="@+id/cv_cardview"
            app:cardCornerRadius="20dp"
            app:cardElevation="20dp"
            android:layout_centerInParent="true">
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/aa"
                android:scaleType="centerInside"/>
        </android.support.v7.widget.CardView>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp">
    
            <SeekBar
                android:layout_width="200dp"
                android:layout_height="wrap_content"
                android:id="@+id/sb_1"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="控制圆角大小"/>
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp">
    
            <SeekBar
                android:layout_width="200dp"
                android:layout_height="wrap_content"
                android:id="@+id/sb_2"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="控制阴影大小"/>
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp">
    
            <SeekBar
                android:layout_width="200dp"
                android:layout_height="wrap_content"
                android:id="@+id/sb_3"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="控制图片间距大小"/>
        </LinearLayout>
    
    
    
    </LinearLayout>
    

    重要属性:

    app:cardCornerRadius 设置圆角的半径
    app:cardElevation 设置阴影的半径
    

    其它属性:

     app:cardBackgroundColor=""设置背景色
            app:cardMaxElevation="" 设置Z轴最大高度值
            app:cardUseCompatPadding="" 是否使用CompatPadding
            app:cardPreventCornerOverlap="" 是否使用PreventCornerOverlap
            app:contentPadding="" 内容的Padding
            app:contentPaddingTop="" 内容的上Padding
            app:contentPaddingLeft="" 内容的左Padding
            app:contentPaddingRight="" 内容的右Padding
            app:contentPaddingBottom="" 内容的下Padding
    

    java代码:

    package com.example.cardviewdemo;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.support.v7.widget.CardView;
    import android.widget.SeekBar;
    
    public class MainActivity extends AppCompatActivity {
    
       /* app:cardBackgroundColor=""设置背景色
        app:cardMaxElevation="" 设置Z轴最大高度值
        app:cardUseCompatPadding="" 是否使用CompatPadding
        app:cardPreventCornerOverlap="" 是否使用PreventCornerOverlap
        app:contentPadding="" 内容的Padding
        app:contentPaddingTop="" 内容的上Padding
        app:contentPaddingLeft="" 内容的左Padding
        app:contentPaddingRight="" 内容的右Padding
        app:contentPaddingBottom="" 内容的下Padding*/
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            final CardView mCardview = (CardView) findViewById(R.id.cv_cardview);
            SeekBar sb_1 = (SeekBar) findViewById(R.id.sb_1);
            SeekBar sb_2 = (SeekBar) findViewById(R.id.sb_2);
            SeekBar sb_3 = (SeekBar) findViewById(R.id.sb_3);
    
            sb_1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                    mCardview.setRadius(i);//设置圆角半径
                }
    
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
    
                }
    
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
    
                }
            });
    
            sb_2.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                    mCardview.setCardElevation(i);//设置阴影半径
                }
    
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
    
                }
    
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
    
                }
            });
    
            sb_3.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                    mCardview.setContentPadding(i, i, i, i);//设置cardView中子控件和父控件的距离
                }
    
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
    
                }
    
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
    
                }
            });
        }
    }
    

    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680

    参考:https://www.cnblogs.com/changyiqiang/p/8884893.html
    https://www.jianshu.com/p/f7d0a4c52ca2

  • 相关阅读:
    【面积并】 Atlantis
    【动态前k大 贪心】 Gone Fishing
    【复杂枚举】 library
    【双端队列bfs 网格图建图】拯救大兵瑞恩
    【奇偶传递关系 边带权】 奇偶游戏
    【权值并查集】 supermarket
    CF w4d3 A. Pythagorean Theorem II
    CF w4d2 C. Purification
    CF w4d2 B. Road Construction
    CF w4d2 A. Cakeminator
  • 原文地址:https://www.cnblogs.com/Android-Alvin/p/11952999.html
Copyright © 2011-2022 走看看