LoginSignup
52
49

More than 5 years have passed since last update.

RecyclerViewを使うときに起きた問題を解決する方法

Posted at

Android LにはListViewの進化型であるRecyclerViewというウィジットが追加されました。
RecyclerViewについて調べると、ListViewに比べると

  • パフォーマンスの向上
  • 使いやすさの向上

といった記事が目につきます。

実際にどうなのか、使うまでの手順や気がついたことを記していきます。

※ 本記事は以下の環境で実施されました。

IDE: Android Studio 0.8.8
Java: 1.6.0_65

※ Android DevelopersにはRecyclerViewのトレーニングとして Creating Lists and Cards というのがあります。

compileSdkVersion android-L の回避方法

Android Studioで新しいProjectで Minimum SDKAPI 20+: Android L に設定する。

作成したときの build.gradle を確認すると次の通りです。

build.gradle
android {
    compileSdkVersion 'android-L'
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "jp.co.samril.myapplication"
        minSdkVersion 'L'
        targetSdkVersion 'L'
        versionCode 1
        versionName "1.0"
    }
    ...
}

実は、このまま作成されたProjectをコンパイルすると次のようなエラーが発生する。

Error:compileSdkVersion android-L requires compiling

調べてみると、バグらしいですね。

[How-to] Use the v21 Support Libs on Older Versions & Target L While Remaining Backwards-Compatible

build.gradle を次のように修正すればOKです。

build.gradle
android {
    compileSdkVersion 20 // ★ここを整数に
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "jp.co.samril.myapplication"
        minSdkVersion 'L'
        targetSdkVersion 'L'
        versionCode 1
        versionName "1.0"
    }
    ...
}

低いSDKでコンパイルする方法

RecyclerViewはAndroid Lから追加されたウィジットのため、SDK Versionが 20 以上でないといけません。

build.gradle の dependencies に次をセットし、

build.gradle
dependencies {
    compile 'com.android.support:support-v4:20.+'
    compile 'com.android.support:recyclerview-v7:+'
}

実際に低いSDKを設定すると、次のようなコンパイルエラーが発生します。

Error:Execution failed for task '...'.
> Manifest merger failed : uses-sdk:minSdkVersion 14 cannot be smaller than version L declared in library com.android.support:support-v4:21.0.0-rc1

これを解除するには AndroidManifest.xml に uses-sdk の設定を追加してやればよい。

AndroidManifest.xml
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="jp.co.homes.android3" >

    <uses-sdk tools:node="replace" />

    ...

xmlns:tools="http://schemas.android.com/tools"<uses-sdk tools:node="replace" /> を追記しました。
これで Gradle のビルドは通るようになります。

レイアウトへの配置したときにプレビューが見れない

レイアウトファイルには、通常のListViewと同じ記述で書くことができます。

layout.xml
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

ただし、Preview を確認しようとするとjava.lang.NullPointerExceptionが発生します。

Rendering Problems
The new RecyclerView does not yet work in Studio. We are working on a fix. (Open Issue 72117, Show Exception)

リンク先に書いてあるとおり、なんとRecyclerViewは Android Studio ではまだ動作しないようです。(まじっすか...

表示したときに発生するNullPointerExceptionを回避する方法

RecyclerViewを埋め込み、無事ビルドまでパスします。
では実機やGenymotionで動作を確認してみようと、画面を表示するとjava.lang.NullPointerExceptionでクラッシュしました。

java.lang.NullPointerException
        at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:1310)
        at android.view.View.measure(View.java:16497)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1404)
    ...

調べてみると、これもAndroid Studioのバグだとか...

Using CardView and RecyclerView in my layout files throws an exception

原因はRecyclerViewを初期化できていないらしい。
回避方法はこちら

***Activity.java
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.my_recycler_view);
recyclerView.setHasFixedSize(true); // RecyclerViewのサイズを維持し続ける
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setItemAnimator(new DefaultItemAnimator());

この実装はonCreate() or onCreateView()に追記します。
これで NullPointerException を回避できます。

行間に区切り線を表示させる方法

RecyclerViewandroid.R.layout.simple_list_item_1 を描画した場合、行間の区切り線が表示されません。

Genymotion_for_personal_use_-_Samsung_Galaxy_S5_-_4_4_2_-_API_19_-_1080x1920__1080x1920__480dpi__-_192_168_56_101.png

これを回避するには RecyclerView.ItemDecoration を使います。

RecyclerView.ItemDecoration とは

Adapterでセットする特定ビューへの描画やレイアウトの追加ができるものです
例えば、今回のような区切り線やハイライトなど、様々な表現を可能にできます

https://gist.github.com/alexfu/0f464fc3742f134ccd1e を参考に次のクラスを作成します。

MyItemDecoration.java
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * Created by samukaak on 2014/11/20.
 */
public class MyItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };

    private Drawable mDivider;

    public MyItemDecoration(final Context context) {
        final TypedArray array = context.obtainStyledAttributes(ATTRS);
        mDivider = array.getDrawable(0);
        array.recycle();
    }

    @Override
    public void getItemOffsets(final Rect outRect, final int itemPosition, final RecyclerView parent) {
        outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
    }

    @Override
    public void onDraw(final Canvas c, final RecyclerView parent) {
        drawVertical(c, parent);
    }

    public void drawVertical(final Canvas c, final RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
}

それを RecyclerView#addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration decor) を使って設定します。

***Activity.java
recyclerView.addItemDecoration(new MyItemDecoration(getActivity()));

Genymotion_for_personal_use_-_Samsung_Galaxy_S5_-_4_4_2_-_API_19_-_1080x1920__1080x1920__480dpi__-_192_168_56_101.png

参考
52
49
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
49