Android通过RecyclerView实现手风琴效果

596 阅读7分钟

Android通过RecyclerView实现手风琴效果

  • Android中RecyclerView动态列表中点击每一项Item展开列表详细内容
  • 关于RecyclerView使用二,通过RecyclerView 实现简单的手风琴折叠效果的一个记录。
  • 对于ExpandableListView又称可扩展的ListView,即每一个一级布局下都可以展开对应的二级布局。每一级布局都需要单独一个布局文件。
  • 对于简单的手风琴效果展示的列表内容,喜欢用RecyclerView实现,这样可以偷懒,少写布局文件。
  • 只是想用RecyclerView实现ExpandableListView的效果,提供一种实现方法和思路。
  • 简单实现手风琴效果如下:

rvzd1.gif

  • RecyclerView页面布局:
          <?xml version="1.0" encoding="utf-8"?>
          <androidx.constraintlayout.widget.ConstraintLayout 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"
              tools:context=".ui.equipment.EquipmentJobLoggingActivity">
    
              <androidx.recyclerview.widget.RecyclerView
                  android:id="@+id/wareHouse_scrap_fra"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  app:layout_constraintEnd_toEndOf="parent"
                  app:layout_constraintStart_toStartOf="parent"
                  app:layout_constraintTop_toTopOf="parent" />
    
          </androidx.constraintlayout.widget.ConstraintLayout>
    
  • RecyclerView列表的每一项布局文件
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content">
        <androidx.cardview.widget.CardView
            android:id="@+id/Card"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="10dp"
            android:layout_marginTop="5dp"
            android:layout_marginEnd="10dp"
            android:layout_marginBottom="5dp"
            android:translationZ="2dp"
            app:cardCornerRadius="10dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">
            <TextView
                android:id="@+id/person_time"
                android:layout_width="50dp"
                android:layout_height="wrap_content"
                android:layout_gravity="end"
                android:background="@color/aurantius"
                android:gravity="center"
                android:rotation="45"
                android:text="唐代"
                android:textColor="@color/white"
                android:textSize="10sp"
                android:translationX="13dp"
                android:translationY="7dp"
                android:translationZ="10dp" />
            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:padding="5dp"
                android:paddingStart="@dimen/f_mp_10_lr"
                android:paddingEnd="@dimen/f_mp_10_lr"
                android:translationZ="2dp">
                <TextView
                    android:id="@+id/title"
                    android:layout_width="wrap_content"
                    android:layout_height="30dp"
                    android:layout_marginStart="10dp"
                    android:layout_marginTop="10dp"
                    android:gravity="center_vertical"
                    android:text=""
                    android:textSize="18sp"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />
                <TextView
                    android:id="@+id/title_person"
                    android:layout_width="wrap_content"
                    android:layout_height="25dp"
                    android:layout_marginTop="25dp"
                    android:layout_marginEnd="10dp"
                    android:gravity="center_vertical"
                    android:text=""
                    android:textSize="14sp"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />
                <LinearLayout
                    android:id="@+id/msg_ll"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="10dp"
                    android:layout_marginTop="10dp"
                    android:layout_marginEnd="10dp"
                    android:layout_marginBottom="10dp"
                    android:orientation="vertical"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/title">
                    <View
                        android:layout_width="match_parent"
                        android:layout_height="1dp"
                        android:layout_marginTop="4dp"
                        android:background="@color/divider" />
                    <TextView
                        android:id="@+id/msg_contentMore"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="5dp"
                        android:layout_marginTop="4dp"
                        android:layout_marginRight="5dp"
                        android:layout_marginBottom="10dp"
                        android:text=""
                        android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
                </LinearLayout>
            </androidx.constraintlayout.widget.ConstraintLayout>
        </androidx.cardview.widget.CardView>
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  • 设置适配器 ,实现点击折叠展示(其实是通过控制隐藏和显示布局实现手风琴折叠效果)
      //数据源 实体类
      public class MsgBean {
        private  String person_time;
        private  String person;
        private  String content;
        private  String contentMore;
        //构造函数 ,getter 、setter 方法省略不展示、
      }
    
      import android.content.Context;
      import android.view.LayoutInflater;
      import android.view.View;
      import android.view.ViewGroup;
      import android.widget.LinearLayout;
      import android.widget.RelativeLayout;
      import android.widget.TextView;
    
      import androidx.cardview.widget.CardView;
      import androidx.recyclerview.widget.RecyclerView;
      import java.util.ArrayList;
      import java.util.List;
      //部分涉及包命名空间的,则不在这里显示
      /**
      * @Description: 适配器
      * @Author: xiaozhao
      * @CreateDate: 2022-10-28 9:56
      * @Version:
      */
      public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder> {
    
          private Context context;
          /**
          * 消息列表数据
          */
          private List<MsgBean> lists;
    
          /**
          * 标记展开的item
          */
          private int opened = -1;
    
          public MsgAdapter(Context context,List<MsgBean> lists) {
              this.context = context;
      //        lists = new ArrayList<>();
              this.lists = lists;
          }
    
          @Override
          public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
              View view = LayoutInflater.from(context).inflate(R.layout.msg_item, parent, false);
              return new ViewHolder(view);
          }
    
          @Override
          public void onBindViewHolder(final ViewHolder holder, int position) {
              holder.bindView(position,lists.get(position));
          }
    
          @Override
          public int getItemCount() {
              return lists.size();
          }
          
          class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
              private TextView person_time,title,title_person,msg_contentMore;
              private LinearLayout msgLl;
              private CardView Card;
              
              public ViewHolder(View itemView) {
                  super(itemView);
                  person_time =  itemView.findViewById(R.id.person_time);
                  title =  itemView.findViewById(R.id.title);
                  title_person =  itemView.findViewById(R.id.title_person);
                  msg_contentMore =  itemView.findViewById(R.id.msg_contentMore);
                  Card =  itemView.findViewById(R.id.Card);
                  msgLl = itemView.findViewById(R.id.msg_ll);
                  Card.setOnClickListener(this);
              }
    
              /**
              * 此方法实现列表数据的绑定和item的展开/关闭
              */
             public void bindView(int pos, MsgBean bean) {
                  //设置内容值
                  person_time.setText(bean.getPerson_time());
                  title.setText(bean.getContent());
                  title_person.setText(bean.getPerson());
                  msg_contentMore.setText(bean.getContentMore());
    
                  if (pos == opened){
                      msgLl.setVisibility(View.VISIBLE);//展示详细内容布局
                  } else{
                      msgLl.setVisibility(View.GONE);//隐藏详细内容布局
                  }
    
              }
              /**
              * item的点击事件
              * @param v
              */
              @Override
              public void onClick(View v) {
                  if (opened == getAbsoluteAdapterPosition()) {
                      //当点击的item已经被展开了, 就关闭.
                      opened = -1;
                      //通知所有注册观察员位置上的项目已发生变化。 相当于调用notifyItemChanged(position, null);
                      //参数 :position -已更改项的位置
                      notifyItemChanged(getAbsoluteAdapterPosition());
                  } else {
                      int oldOpened = opened;
                      opened = getAbsoluteAdapterPosition();
                      notifyItemChanged(oldOpened);//通知所有注册观察员位置上的项目已发生变化。 相当于调用notifyItemChanged(position, null);//参数 :position -已更改项的位置
                      notifyItemChanged(opened);
                  }
              }
          }
      }
    
  • 在Fragment中实现代码
      import android.app.Activity;
      import android.content.Context;
      import android.content.Intent;
      import android.os.Bundle;
      import androidx.annotation.NonNull;
      import androidx.fragment.app.Fragment;
      import androidx.recyclerview.widget.LinearLayoutManager;
      import androidx.recyclerview.widget.RecyclerView;
      import android.view.LayoutInflater;
      import android.view.View;
      import android.view.ViewGroup;
      import java.util.ArrayList;
      import java.util.List;
      //部分涉及包命名空间的,则不在这里显示
      public class WarehouseFragment extends Fragment {
          private Activity myActivity;
          private View rootView;
          private RecyclerView setCe;
          private MsgAdapter msgAdapter;
    
          @Override
          public void onAttach(@NonNull Context context) { //在Fragment和Activity关联上的时候调用,且仅调用一次。
              this.myActivity = (Activity) context;
              super.onAttach(context);
          }
    
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              if (getArguments() != null) {
    
              }
          }
    
          @Override
          public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                  Bundle savedInstanceState) {
              //防止重复解析,造成资源浪费
              if (rootView == null) {
                  rootView = inflater.inflate(R.layout.fragment_warehouse, container, false);
              }
              initView();
              return rootView;
          }
    
          // 初始化 视图控件
          private void initView() {
              setCe = rootView.findViewById(R.id.wareHouse_scrap_fra);
              setRecyclerViewAdapter();// 创建  RecyclerView 适配器
          }
    
          //创建  RecyclerView 适配器
          private void setRecyclerViewAdapter() {
              setCe.setLayoutManager(getLinearLayoutManager());
              msgAdapter = new MsgAdapter(myActivity, getArrayList4());
              setCe.setAdapter(msgAdapter);
              //在详细内容显示于隐藏的时候就有个打开和关闭的动画.
              setCe.getItemAnimator().setChangeDuration(300);
              setCe.getItemAnimator().setMoveDuration(300);
          }
    
          // RecyclerView 适配器的线性布局
          private LinearLayoutManager getLinearLayoutManager() {
              //设置线性布局//网格布局 GridLayoutManager //瀑布流网格布局 StaggeredGridLayoutManager
              LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false);
              return layoutManager;
          }
          // 模拟数据源
          private List<MsgBean> getArrayList4() {
              List<MsgBean> dataList = new ArrayList<>();
              dataList.add(new MsgBean( "唐代","李白", "将进酒",
                      "君不见黄河之水天上来,奔流到海不复回。\n" +
                      "君不见高堂明镜悲白发,朝如青丝暮成雪。\n" +
                      "人生得意须尽欢,莫使金樽空对月。\n" +
                      "天生我材必有用,千金散尽还复来。\n" +
                      "烹羊宰牛且为乐,会须一饮三百杯。\n" +
                      "岑夫子,丹丘生,将进酒,杯莫停。\n" +
                      "与君歌一曲,请君为我倾耳听。(倾耳听 一作:侧耳听)\n" +
                      "钟鼓馔玉不足贵,但愿长醉不愿醒。(不足贵 一作:何足贵;不愿醒 一作:不复醒)\n" +
                      "古来圣贤皆寂寞,惟有饮者留其名。(古来 一作:自古;惟 通:唯)\n" +
                      "陈王昔时宴平乐,斗酒十千恣欢谑。\n" +
                      "主人何为言少钱,径须沽取对君酌。\n" +
                      "五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。"));
              dataList.add(new MsgBean( "两汉","曹操",  "短歌行","对酒当歌,人生几何!\n" +
                      "譬如朝露,去日苦多。\n" +
                      "慨当以慷,忧思难忘。\n" +
                      "何以解忧?唯有杜康。(唯 一作:惟)\n" +
                      "青青子衿,悠悠我心。\n" +
                      "但为君故,沉吟至今。\n" +
                      "呦呦鹿鸣,食野之苹。\n" +
                      "我有嘉宾,鼓瑟吹笙。\n" +
                      "明明如月,何时可掇?(明明 一作:皎皎)\n" +
                      "忧从中来,不可断绝。\n" +
                      "越陌度阡,枉用相存。\n" +
                      "契阔谈讌,心念旧恩。(谈讌 一作:谈宴)\n" +
                      "月明星稀,乌鹊南飞。\n" +
                      "绕树三匝,何枝可依?\n" +
                      "山不厌高,海不厌深。\n" +
                      "周公吐哺,天下归心。"));
              dataList.add(new MsgBean( "两汉","诸葛亮", "出师表"," 先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。\n" +
                      "\n" +
                      "  宫中府中,俱为一体;陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。\n" +
                      "\n" +
                      "  侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。\n" +
                      "\n" +
                      "  将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。\n" +
                      "\n" +
                      "  亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。\n" +
                      "\n" +
                      "  臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。\n" +
                      "\n" +
                      "  先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。\n" +
                      "\n" +
                      "  愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏。臣不胜受恩感激。今当远离,临表涕零,不知所言。"));
              dataList.add(new MsgBean( "苏轼","宋代", "水调歌头·明月几时有","丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。\n" +
                      "\n" +
                      "明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。\n" +
                      "转朱阁,低绮户,照无眠。不应有恨,何事长向别时圆?人有悲欢离合,月有阴晴圆缺,此事古难全。但愿人长久,千里共婵娟。"));
              dataList.add(new MsgBean( "韩愈","唐代", "师说","古之学者必有师。师者,所以传道受业解惑也。人非生而知之者,孰能无惑?惑而不从师,其为惑也,终不解矣。生乎吾前,其闻道也固先乎吾,吾从而师之;生乎吾后,其闻道也亦先乎吾,吾从而师之。吾师道也,夫庸知其年之先后生于吾乎?是故无贵无贱,无长无少,道之所存,师之所存也。\n" +
                      "\n" +
                      "  嗟乎!师道之不传也久矣!欲人之无惑也难矣!古之圣人,其出人也远矣,犹且从师而问焉;今之众人,其下圣人也亦远矣,而耻学于师。是故圣益圣,愚益愚。圣人之所以为圣,愚人之所以为愚,其皆出于此乎?爱其子,择师而教之;于其身也,则耻师焉,惑矣。彼童子之师,授之书而习其句读者,非吾所谓传其道解其惑者也。句读之不知,惑之不解,或师焉,或不焉,小学而大遗,吾未见其明也。巫医乐师百工之人,不耻相师。士大夫之族,曰师曰弟子云者,则群聚而笑之。问之,则曰:“彼与彼年相若也,道相似也,位卑则足羞,官盛则近谀。”呜呼!师道之不复,可知矣。巫医乐师百工之人,君子不齿,今其智乃反不能及,其可怪也欤!\n" +
                      "\n" +
                      "  圣人无常师。孔子师郯子、苌弘、师襄、老聃。郯子之徒,其贤不及孔子。孔子曰:三人行,则必有我师。是故弟子不必不如师,师不必贤于弟子,闻道有先后,术业有专攻,如是而已。\n" +
                      "\n" +
                      "  李氏子蟠,年十七,好古文,六艺经传皆通习之,不拘于时,学于余。余嘉其能行古道,作《师说》以贻之。"));
              return dataList;
          }
      }