user3746428
user3746428

Reputation: 11175

ListView rows with rounded corners

I am currently working on rewriting one of my iOS apps to release on Android. I'm making good progress but I am looking for some guidance regarding the best way to approach the list rows.

These are the cells/rows I am trying to recreate:

enter image description here

As you can see, I have a white background view with padding around each edge and rounded corners, so I guess for this I would need to embed everything within a view of some sort? The other part I am unsure about is how to create the coloured circle on the right. Would this be another view with rounded corners and a coloured background? I haven't managed to figure out how to get this to the right of the two textViews that I currently have. So if anyone could give a code example then that would be great.

This is my current XML:

<ImageView
    android:layout_width="78dp"
    android:layout_height="78dp"
    android:layout_alignParentTop="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:scaleType="centerCrop"
    android:adjustViewBounds="false"
    android:id="@+id/attractionImageView"
    android:contentDescription="Attraction Image"
    android:background="@color/colorPrimary" />

<ImageView
    android:layout_width="20dp"
    android:layout_height="20dp"
    android:layout_marginLeft="5dp"
    android:layout_marginTop="5dp"
    android:background="@drawable/fast_pass"
    android:id="@+id/fastPassImageView" />

<LinearLayout android:orientation="vertical" android:layout_height="match_parent" android:layout_width="fill_parent" android:layout_toEndOf="@+id/attractionImageView" android:layout_toRightOf="@+id/attractionImageView">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="49dp"
        android:id="@+id/attractionNameTextView"
        android:text="Attraction Name"
        android:layout_marginLeft="5dp"/>

    <LinearLayout android:orientation="horizontal" android:layout_height="match_parent" android:layout_width="fill_parent" android:layout_toEndOf="@+id/attractionImageView" android:layout_toRightOf="@+id/attractionImageView">
        <ImageView
            android:layout_width="13dp"
            android:layout_height="13dp"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:id="@+id/updatedImageView"
            android:background="@drawable/updated"
            android:layout_gravity="center_vertical"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="29dp"
            android:id="@+id/updatedTextView"
            android:text="Updated"
            android:gravity="center_vertical"/>
    </LinearLayout>
</LinearLayout>

This seems to work well for everything I've got so far, but I'm not sure where to go from here. This is how it looks:

enter image description here

Any suggestions?

Upvotes: 0

Views: 1792

Answers (3)

Daniel Nugent
Daniel Nugent

Reputation: 43342

As an alternate solution, I just got something working with CardViews and using Picasso with a CircleTransformation for the circle.

In this simple example, I used a RecyclerView of CardViews.

First, here is the CircleTransformation class that is used by Picasso:

import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;

import com.squareup.picasso.Transformation;

public class CircleTransform implements Transformation {

    @Override
    public Bitmap transform(Bitmap source) {
        int size = Math.min(source.getWidth(), source.getHeight());

        int x = (source.getWidth() - size) / 2;
        int y = (source.getHeight() - size) / 2;

        Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);

        if (squaredBitmap != source) {
            source.recycle();
        }

        Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());

        Canvas canvas = new Canvas(bitmap);
        BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG);
        paint.setShader(shader);

        float r = size/2f;
        canvas.drawCircle(r, r, r-1, paint);

        Paint paintBorder = new Paint();
        paintBorder.setStyle(Style.STROKE);
        paintBorder.setColor(Color.argb(84,0,0,0));
        paintBorder.setAntiAlias(true);
        paintBorder.setStrokeWidth(1);
        canvas.drawCircle(r, r, r-1, paintBorder);

        squaredBitmap.recycle();
        return bitmap;
    }

    @Override
    public String key() {
        return "circle";
    }
}

Here is the Fragment:

public class BlankFragment extends Fragment {

    public BlankFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View rootView = inflater.inflate(R.layout.fragment_blank, container, false);

        RecyclerView rv = (RecyclerView) rootView.findViewById(R.id.rv_recycler_view);
        rv.setHasFixedSize(true);
        MyAdapter adapter = new MyAdapter(new String[]{"testone", "testtwo", "testthree", "testfour"}, getActivity());
        rv.setAdapter(adapter);

        LinearLayoutManager llm = new LinearLayoutManager(getActivity());
        rv.setLayoutManager(llm);

        return rootView;
    }

}

fragment_blank.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" 
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
       </android.support.v7.widget.RecyclerView>   
</RelativeLayout>

The adapter, which includes the defaultCircleWithText() method for drawing the circle and text. Here I'm just using one color for the circles, but you can extend this to set the correct circle color for each row:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private String[] mDataset;
    Context mContext;

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public static class MyViewHolder extends RecyclerView.ViewHolder {
        public CardView mCardView;
        public TextView mTextView;
        public ImageView mImageView;


        public MyViewHolder(View v) {
            super(v);

            mCardView = (CardView) v.findViewById(R.id.card_view);
            mTextView = (TextView) v.findViewById(R.id.tv_text);
            mImageView = (ImageView) v.findViewById(R.id.iv_image);
        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(String[] myDataset, Context context) {
        mDataset = myDataset;
        mContext = context;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext())
                               .inflate(R.layout.card_item, parent, false);
        // set the view's size, margins, paddings and layout parameters
        MyViewHolder vh = new MyViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.mTextView.setText(mDataset[position]);
        Drawable drawable = new BitmapDrawable(mContext.getResources(), defaultCircleWithText("test"));
        Picasso.with(mContext).load((String)null).fit().transform(new CircleTransform()).placeholder(drawable).into(holder.mImageView);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }

    public final static int BIG_IMAGE = 138;
    public static Bitmap defaultCircleWithText(String text) {

        int size = BIG_IMAGE;
        Bitmap image = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(image);

        int alpha = 165;

        //hard-coded circle color:
        int color = Color.argb(alpha,253,70,45);

        Paint p_circle = new Paint();
        p_circle.setAntiAlias(true);
        p_circle.setColor(color);
        c.drawCircle(size/2f, size/2f, size/2f-1, p_circle);

        Paint p_text = new Paint();
        p_text.setAntiAlias(true);
        p_text.setColor(Color.WHITE);
        p_text.setTextSize(58);

        RectF bounds = new RectF(0, 0, c.getWidth(), c.getHeight());
        // measure text width
        bounds.right = p_text.measureText(text, 0, text.length());
        // measure text height
        bounds.bottom = p_text.descent() - p_text.ascent();

        bounds.left += (c.getWidth() - bounds.right) / 2.0f;
        bounds.top += (c.getHeight() - bounds.bottom) / 2.0f;
        c.drawText(text, bounds.left, bounds.top - p_text.ascent(), p_text);

        return image;
    }
}

card_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="78dp" >

    <android.support.v7.widget.CardView
        android:id="@+id/card_view"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        card_view:cardCornerRadius="4dp"
        android:layout_width="match_parent"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

        <TextView 
            android:id="@+id/tv_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="test" >       
            </TextView>


        <ImageView
            android:id="@+id/iv_image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true" 
            android:layout_marginRight="10dp"/>

      </RelativeLayout>
      </android.support.v7.widget.CardView>


</RelativeLayout>

Result:

enter image description here

Upvotes: 0

Coder
Coder

Reputation: 166

I suppose that you're using custom views for your listview's items, if so, I would set the background of the custom view to something like this:

<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="@color/white" />

<stroke android:width="4dp" android:color="@color/beige" /> //If you want a stroke

<corners
    android:topLeftRadius="20dp"
    android:topRightRadius="20dp"
    android:bottomLeftRadius="20dp"
    android:bottomRightRadius="20dp" />

<padding 
    android:left="10dp" 
    android:top="10dp" 
    android:right="10dp"                  
    android:bottom="10dp" />

Upvotes: 1

Gabe Sechan
Gabe Sechan

Reputation: 93728

For the colored circle- I'd suggest just a text view set to the proper size with a circle drawable set as the background of the view. Should get what you want.

For the background- I'd just stick the entire row in either a linear or relative layout, then set a RoundedBitmapDrawable as the background of the layout. That will give you the rounded background effect. If necessary add some margin to the top and bottom of each view to increase the gap between items.

Upvotes: 1

Related Questions