Search code examples
androidhorizontal-scrollingsmooth-scrolling

Android App: Most Efficient Way to Handle Horizontal Scrolling With Lots of Images


I'm wondering if there's a more efficient way to handle horizontal scrolling with a good number of images.

Right now I'm using a HorizontalScrollView component with a Frame Layout as its only child. Then I'm laying out the ImageViews on the Layout. It works but I'm noticing that on certain devices the scrolling is very choppy but on others its very smooth.

So I was wondering if there is a better and more efficient way to handle it.

Some code snippets:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/app_background"
tools:context="com.toyota.fanmojis.PostActivity">

<RelativeLayout
    android:layout_height="265dp"
    android:layout_width="match_parent"
    android:layout_alignParentBottom="true"
    android:id="@+id/keyboardLayout">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="#C9C9C9"
        android:layout_alignParentTop="true"
        android:id="@+id/shareLayout">

        <RelativeLayout
            android:id="@+id/socialLayout"
            android:layout_centerHorizontal="true"
            android:layout_width="217dp"
            android:layout_height="40dp">

            <ImageButton
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:id="@+id/fb_button"
                android:layout_marginLeft="8dp"
                android:layout_centerVertical="true"
                android:background="@drawable/fb_color"
                android:scaleType="fitXY"
                android:contentDescription="Post via Facebook" />

            <ImageButton
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:id="@+id/twitter_button"
                android:layout_toRightOf="@id/fb_button"
                android:layout_marginLeft="10dp"
                android:layout_centerVertical="true"
                android:background="@drawable/twitter_color"
                android:scaleType="fitXY"
                android:contentDescription="Post via Twitter" />

            <ImageButton
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:id="@+id/sms_button"
                android:layout_toRightOf="@id/twitter_button"
                android:layout_marginLeft="10dp"
                android:layout_centerVertical="true"
                android:background="@drawable/sms_color"
                android:scaleType="fitXY"
                android:contentDescription="Post via SMS" />

            <ImageButton
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:id="@+id/email_button"
                android:layout_toRightOf="@id/sms_button"
                android:layout_marginLeft="10dp"
                android:layout_centerVertical="true"
                android:background="@drawable/email_color"
                android:scaleType="fitXY"
                android:contentDescription="Post via SMS" />

            <ImageButton
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:id="@+id/copy_paste_button"
                android:layout_toRightOf="@id/email_button"
                android:layout_marginLeft="10dp"
                android:layout_centerVertical="true"
                android:background="@drawable/copy"
                android:scaleType="fitXY"
                android:contentDescription="Post via SMS" />

        </RelativeLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="12sp"
            android:text="@string/share"
            android:id="@+id/textView"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@+id/socialLayout"
           />

    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/fanmojisLayout"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_below="@id/shareLayout">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="32dp"
            android:id="@+id/bottomViewLayout"
            android:layout_alignParentBottom="true"
            android:background="@drawable/red_background">

            <ImageButton
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:id="@+id/homeButton"
                android:layout_centerVertical="true"
                android:layout_alignParentLeft="true"
                android:background="@drawable/home"
                android:scaleType="fitXY"
                android:contentDescription="Go back to home" />

            <ImageButton
                android:layout_width="44dp"
                android:layout_height="31dp"
                android:id="@+id/backspaceButton"
                android:layout_centerVertical="true"
                android:layout_alignParentRight="true"
                android:background="@drawable/delete_selector"
                android:layout_marginRight="1dp"
                android:layout_marginLeft="4dp"
                android:scaleType="fitXY"
                android:contentDescription="Go back to home" />

            <Button
                android:layout_width="44dp"
                android:layout_height="31dp"
                android:id="@+id/clearButton"
                android:layout_centerVertical="true"
                android:layout_toLeftOf="@id/backspaceButton"
                android:background="@drawable/clear_selector"
                android:scaleType="fitXY"
                android:contentDescription="Go back to home" />

        </RelativeLayout>

            <com.upinteractive.fanmojis.components.FanmojiHorizontalScrollView
            android:id="@+id/fanmojisScrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@id/bottomViewLayout"
            android:scrollbars="none"
            android:animationCache="false"
            android:orientation="horizontal">

                <FrameLayout
                    android:id="@+id/fanmojiContentLayout"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"/>

        </com.upinteractive.fanmojis.components.FanmojiHorizontalScrollView>

    </RelativeLayout>

</RelativeLayout>

<com.upinteractive.fanmojis.components.ChatBubble
    android:id="@+id/chatBubble"
    android:layout_width="220dp"
    android:layout_height="100dp"
    android:layout_centerHorizontal="true"
    android:layout_alignParentTop="true"
    android:background="@drawable/chat"/>

<RelativeLayout
    android:id="@+id/errorLayout"
    android:layout_width="300dp"
    android:layout_height="180dp"
    android:layout_marginTop="80dp"
    android:layout_centerHorizontal="true"
    android:background="@drawable/error_layout_bg"
    android:visibility="invisible">

    <TextView
        android:id="@+id/errorText"
        android:textColor="@android:color/white"
        android:textAlignment="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>
</RelativeLayout>

And the code that loads the frame layout with the images:

protected void setupScroll() {

    double hspacing = (displaymetrics.widthPixels - (IMAGE_SIZE * 6)) / 7;
    double hfixedspacing = hspacing;
    double vspacing = 5.0;
    double max = 0;

    double topSpacing = 20.0;
    double xPosition = hspacing;
    double yPosition = topSpacing;

    int row = 0;
    int col = 0;

    int viewcount = imageArray.length() / 18;

    int mod = (imageArray.length() % 18);
    if (mod > 6)
        viewcount++;

    FrameLayout fl = (FrameLayout) this.findViewById(R.id.fanmojiContentLayout);
    fl.setBackgroundColor(getResources().getColor(R.color.kb_bg_color));
    scrollView.setBackgroundColor(getResources().getColor(R.color.kb_bg_color));

    int inSampleSize = 0;

    try {
        for (int i = 0; i < imageArray.length(); i++) {
            JSONObject emoji = imageArray.getJSONObject(i);

            String basename = emoji.getString("name");
            String fullname = "f" + basename + "_small";

            int resourceID = getResources().getIdentifier(fullname, "drawable", getPackageName());

            if(i == 0){
                final BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeResource(getResources(), resourceID, options);
                inSampleSize = Util.calculateInSampleSize(options,IMAGE_SIZE,IMAGE_SIZE);
            }

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inSampleSize = inSampleSize;
            Bitmap b = BitmapFactory.decodeResource(getResources(), resourceID, options);

            FMImageView iv = new FMImageView(this);
            iv.name = basename;
            iv.fullname = fullname;
            iv.setImageBitmap(b);

            FrameLayout.LayoutParams imageParams = new FrameLayout.LayoutParams(IMAGE_SIZE, IMAGE_SIZE);
            imageParams.leftMargin = (int) xPosition;
            imageParams.topMargin = (int) yPosition;

            fl.addView(iv, imageParams);

            col++;

            if (col > 5) {
                col = 0;
                row++;

                if (row > 2) {
                    xPosition += hspacing + IMAGE_SIZE;
                    hfixedspacing = xPosition;
                } else {
                    xPosition = hfixedspacing;
                }

                yPosition += vspacing + IMAGE_SIZE;
            } else {
                xPosition += hspacing + IMAGE_SIZE;
            }

            if (xPosition > max)
                max = xPosition;

            if (row > 2) {
                xPosition = hfixedspacing;
                yPosition = topSpacing;
                row = 0;
                col = 0;
            }

        }
    } catch (JSONException je) {
        je.printStackTrace();
    }

    fl.setMinimumWidth((int) max + 200);
}

Solution

  • You would probably get better performance with a scrolling component that recycles its views.

    Try using a RecyclerView with a GridLayoutManager that has the orientation property set to "horizontal".

    You'll need to code an adapter for the RecyclerView.