20190811のAndroidに関する記事は2件です。

[Java]RecyclerViewの使用方法とアニメーション付きスワイプ処理の実装をメモ。

1. はじめに

1.1 お断り

このサイトではQiita内の投稿をいくつかリンクさせていただいております。不愉快だと感じた投稿者様はコメントしていただければ幸いです。対応させていただきます。

1.2 目的

RecyclerViewはとても便利です。僕はListViewの代わりに使っているのですがその実装方法とスワイプの実装を忘れてしまうのでここにメモをします。スワイプのアニメーションはGmailのメール削除のスワイプをまねます。

2. RecyclerView本体

この実装内容は「RecyclerViewの基本」by@naoi様と同じです。とても分かりやすく解説されています。RecyclerViewの仕組みは少し複雑なので図も覚えとくといいと思いますよ。

ここでは以下の四つを制作します。
- adapter_row.xml
- DataModel.java
- ViewHolder.java
- Adapter.java

今回、入れるデータは画像と文字列にしましょう。

2.1 一列のレイアウトを決める (adapter_row.xml)

RecyclerViewの一列のレイアウトを決めます。

adapter_row
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/imageView_adapter_show_bitmap"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:id="@+id/textView_adapter_show_string"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>

</android.support.constraint.ConstraintLayout>

2.2 構造体を作る (DataModel.java)

Javaに構造体はありませんが、Classで代用できますよね。

DataModel.java
public class DataModel {
    private Bitmap mBitmap;
    private String mString;

    public Bitmap getBitmap () {
        return mBitmap;
    }

    public String getString () {
        return mString;
    }

    public void setBitmap (Bitmap mBitmap) {
        this.mBitmap = mBitmap;
    }

    public void setString (String mString) {
        this.mString = mString;
    }
}

メンバ変数にmを付ける習慣って一般的にはないんですかね...

余談ですが「get~」とか「set~」っていうのはgetter/setterていうらしいですが、AndroidStudioで、publicまで打つとメンバ変数のgetter/setterを作ってくれるそう。ちょっと名前変えたらそれで完成だからありがたい。

2.3 一行内のViewを取得 (ViewHolder.java)

ViewHolderを作ります。ここでxml本体は指定しませんが一行のなかのViewfindViewByID()します。

ViewHolder.java
public class ViewHolder extends RecyclerView.ViewHolder {
    public ImageView mImageView;
    public TextView mTextView;

    // コンストラクタ
    public ViewHolder (@NonNull View itemView) {
        super(itemView);
        mImageView = itemView.findViewById(R.id.imageView_adapter_show_bitmap);
        mTextView = itemView.findViewById(R.id.textView_adapter_show_string);
    }
}

コンストラクタはRecyclerView.ViewHolderを継承すると引数にitemViewを追加するように要求されます。

2.4 データを配置する (Adapter.java)

Adapter.java
public class Adapter extends RecyclerView.Adapter<ViewHolder> {

    private List<DataModel> insertDataList;

    public Adapter (List<DataModel> list) {
        this.insertDataList = list;
    }

    @NonNull
    @Override
    public NoteViewHolder onCreateViewHolder (@NonNull ViewGroup viewGroup, int i) {
        View inflate = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.adapter_row, viewGroup, false);
        return new ViewHolder(inflate);
    }

    @Override
    public void onBindViewHolder (@NonNull ViewHolder viewHolder, int i) {
        noteViewHolder.mImageView.setImageBitmap(insertDataList.get(i).getBitmap());
        noteViewHolder.mTextView.setText(insertDataList.get(i).getString());
    }

    @Override
    public int getItemCount () {
        return insertDataList.size();
    }
}

3.使い方

Activity等での使い方は以下の通りです。
Adapterは他のと競合してしまうかも...(僕は実際、違う名前を付けている。)

MainActivity.java
public class RegisterNoteActivity extends AppCompatActivity {
    RecyclerView mRecyclerViewList;

    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerViewList = findViewById(R.id.recyclerView_main);

        LinearLayoutManager manager = new LinearLayoutManager(this);
        mRecyclerViewList.setHasFixedSize(false);
        mRecyclerViewList.setLayoutManager(manager);
        mRecyclerViewList.setAdapter(new Adapter(createData()));
    }

    private List<NoteDataModel> createData() {
        List<NoteDataModel> list = new ArrayList<>();
        for (int index = 0; index < 100; index++) {
            // 一行分のデータ
            NoteDataModel rowData = new NoteDataModel();
            rowData.setBitmap(/*なんかの画像*/);
            rowData.setmString("この行は" + (index + 1) + "回目の繰り返しです。");
            list.add(rowData);
        }
        return list;
    }
}

4. スワイプ

アニメーション付きスワイプを実装しましょう。
実をいうともともとスワイプを実装すると横にずれたり、シュッって消えたりするアニメーションはついてきます。色とアイコンはついてこないのでここを実装すればいいということです。
今回は左スワイプの時のみ削除にします。色は赤でアイコンは自前のゴミ箱マークです。

MainActivity.java
    // ...省略    
    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register_note);
        // ...省略...
        mRecyclerViewList.setAdapter(new Adapter(createData())); // さっきの
        // ここから追加
        final Drawable deleteIcon = ContextCompat.getDrawable(this, R.drawable.ic_action_delete);
        ItemTouchHelper.SimpleCallback callback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {

            @Override
            public boolean onMove (@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
                return false;
            }

            @Override
            public void onSwiped (@NonNull RecyclerView.ViewHolder viewHolder, int i) {
                int swipedPosition = viewHolder.getAdapterPosition();
                Adapter adapter = (Adapter) mRecyclerView.getAdapter();
                // 登録とかするんだったらなにかのリストから削除をする処理はここ
                // 削除されたことを知らせて反映させる。
                adapter.notifyItemRemoved(swipedPosition);
            }

            @Override
            public void onChildDraw (@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
                View itemView = viewHolder.itemView;

                // キャンセルされた時
                if (dX == 0f && !isCurrentlyActive) {
                    clearCanvas(c, itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
                    super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, false);
                    return;
                }

                ColorDrawable background = new ColorDrawable();
                background .setColor(Color.parseColor("#f44336"));
                background.setBounds(itemView.getRight() + (int)dX, itemView.getTop(),  itemView.getRight(), itemView.getBottom());
                background.draw(c);

                int deleteIconTop = itemView.getTop() + (itemView.getHeight() - deleteIcon.getIntrinsicHeight()) / 2;
                int deleteIconMargin = (itemView.getHeight() - deleteIcon.getIntrinsicHeight()) / 2;
                int deleteIconLeft = itemView.getRight() - deleteIconMargin - deleteIcon.getIntrinsicWidth();
                int deleteIconRight = itemView.getRight() - deleteIconMargin;
                int deleteIconBottom = deleteIconTop +  deleteIcon.getIntrinsicHeight();

                deleteIcon.setBounds(deleteIconLeft, deleteIconTop, deleteIconRight, deleteIconBottom);
                deleteIcon.draw(c);
            }
        };
        new ItemTouchHelper(callback).attachToRecyclerView(mRecyclerView);

    }

    private void clearCanvas(Canvas c, int left, int top, int right, int bottom) {
        Paint paint = new Paint();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        c.drawRect(left, top, right, bottom, paint);
    }
    // 省略...

アイコンの画像や計算、色コード、処理思想は「Android のリストをスワイプして削除する #あれどうやるの?」by@shts様とそのGithubからいただきました。無断ですみません。

スワイプが完了したときに発火するのはonSwipedですが、それに対してキャンセルしたとしても途中に毎フレームごとに呼ばれるのがonChildDrawです。onMoveはドラッグアンドドロップも許可したとき用だそうですがfalseを返しとけばいいのかな?

追記ですが右スワイプと左スワイプ両方を許可するのは以下の通りにやればいいみたいです。

        ItemTouchHelper.SimpleCallback callback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {

            @Override
            public boolean onMove (@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
                return false;
            }

            @Override
            public void onSwiped (@NonNull RecyclerView.ViewHolder viewHolder, int i) {

            }

            @Override
            public void onChildDraw (@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
                if (dX < 0) {
                    // 左スワイプのとき
                } else {
                    // 右スワイプのとき
                }
            }
        };

隠れてぱっと見では見えないけど、一行目の最後、ItemTouchHelper.LEFT | ItemTouchHelper.RIGHTになってるので気を付けてください。
onChildDrawは上記通りなのですが、onSwipedでの処理の分け方はわからないです。(iを使うのかな?)

5.まとめ

まずは参考にさせていただいたサイトの投稿者様誠にありがとうございました。少し雑ではありますがこちらでまとめて紹介させていただきます。

RecyclerViewの基本
RecyclerView本体についてほぼ同じようにまねさせていただたきました。

RecyclerViewでドラッグアンドドロップの移動とスワイプの削除
スワイプのイメージをつかみました。

Android のリストをスワイプして削除する #あれどうやるの?
スワイプの処理についてJavaに書き直させていただきました。
→画像と詳しい処理は付属のGitHubからです。

Android で RecyclerView を使って横スワイプで要素を削除する方法のメモ
JavaでAdapterをどうやて更新するかを参考にしました。

RecyclerViewのアイテムをスワイプで削除する方法
投稿外での追加の処理について参考にしました。

皆さんありがとうございました。
こうまでして他人のコードを取りまくっていくことでしか処理を生み出せない自分が情けないですが、だからこそだれかのためになれればなと思います。

Twitter: https://twitter.com/Cyber_Hacnosuke (フォローしてくださいお願いします。)
いいねもお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Programming News】Qiitaまとめ記事 Weekly August 2nd week, 2019 Vol.4

筆者が2019/8/4(日)~8/10(土)に気になったQiitaの記事のまとめのまとめを作成しました。日々のまとめ記事のWeekly版です。

皆様が興味のある言語や思いもよらぬハック方法をこの中から見つけられたら幸いです。

Java

Python

Ruby

Rails

C#

Android

Swift

Kotlin

Flutter

Fulx

JavaScript

React

Node.js

Vue.js

Vuex

Nuxt.js

Nest.js

Angular

jQuery

TypeScript

C#

PowerApps

ReactNative

Laravel

C

PHP

CakePHP

Rust

Go言語

R言語

Scala

Haskell

Unity

Spark

Twitter

Line

A-Frame

PowerApp

Line

HTML

CSS

Sass

SQL

MySQL

PostgreSQL

Oracle

MongoDB

SQL Server

Apache

ビッグデータ

Visual Studio Code

IntelliJ IDEA

AI

IoC

Git

AWS

Azure

Oracle Cloud

IBM Cloud

Active Directory

Cloud SQL

Cloud9

インフラ

ブロックチェーン

Ethereum

Hyperledger

セキュリティ

機械学習

自然言語処理

Network

RPA

CI

Docker

Heroku

VirtualBox

kubernetes

OpenStack

Swagger

AMP

OpenID

OAuth2.0

Elasticsearch

Linux

Cent OS

Windows

Mac

Redis

Google API

Google Apps Script

Google Cloud Platform

Google Colaboratory

Google Cloud Data Catalog

Google Drive

VB.Net

Firebase

Server Side

CSS

BootStrap

WordPress

Develop

Keras

PowerShell

Vim

Atom

awk

LaTex

Redmine

UML

Raspberry

RPA

IoT

Alexa

Line

SharePoint

VBA

ShellScript

PowerShell

Slack

Nim

Emacs

WPF

UI

Ansible

Arduino

Julia

Coral

ionic

QRCode

OCR

EC-CUBE

資格

転職

更新情報

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む