androidandroid-listviewhorizontallist

Implementing horizontalList inside a vertical list in android


I have made a custom Horizontal List view which works fine. I then implemented the same horizontal list inside a ListView control. But when I run the same, all I get is a blank output. The list does not seem to poupulate.

On debugging I found, that the adapter which was added to the list view contain the items(Horizontal lists).

So, the problem is clearly not of items not populating properly, its of the view not rendered properly.

Here is the code for vertical list view:

XML:

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

    <com.example.twowaylistview.HorizontalListView
        android:id="@+id/lstHor"
        android:layout_height="100dp"
        android:layout_width="fill_parent">
    </com.example.twowaylistview.HorizontalListView>
</LinearLayout>

and here is XML for individual item in the horizontal list

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="100dp">

    <ImageView
        android:id="@+id/imgListItem"
        android:layout_height="85dp"
        android:layout_width="85dp"
      ..            />

   ...

</RelativeLayout>

let me know if you need the java code. Thanks.


Solution

  • After Android L Developer Preview published, Google supply a new View called RecyclerView which can replace ListView and GridView, it makes it easier to make a horizontal list, so I update my answer here.

    We don't need to use any library anymore at this time, RecyclerView is just enough. Here is the code which I make a horizontal list by RecyclerView (I am not trying to explain detailed usage of RecyclerView):

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        List<List<String>> mDataList;
    
        private RecyclerView mVerticalList;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            prepareData();
            initListView();
        }
    
        private void prepareData() {
            mDataList = new ArrayList<>();
            int vItemCount = 25;
            int hItemCount = 20;
            for (int i = 0; i < vItemCount; i++) {
                List<String> hList = new ArrayList<>();
                for (int j = 0; j < hItemCount; j++) {
                    hList.add("Item." + j);
                }
                mDataList.add(hList);
            }
        }
    
        private void initListView() {
            mVerticalList = (RecyclerView) findViewById(R.id.vertical_list);
            mVerticalList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
            VerticalAdapter verticalAdapter = new VerticalAdapter();
            verticalAdapter.setData(mDataList);
            mVerticalList.setAdapter(verticalAdapter);
        }
    
        private static class VerticalAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
            private List<List<String>> mDataList;
    
            public VerticalAdapter() {
            }
    
            public void setData(List<List<String>> data) {
                mDataList = data;
                notifyDataSetChanged();
            }
    
            private class HorizontalListViewHolder extends RecyclerView.ViewHolder {
    
                private TextView title;
                private RecyclerView horizontalList;
                private HorizontalAdapter horizontalAdapter;
    
                public HorizontalListViewHolder(View itemView) {
                    super(itemView);
                    Context context = itemView.getContext();
                    title = (TextView) itemView.findViewById(R.id.item_title);
                    horizontalList = (RecyclerView) itemView.findViewById(R.id.item_horizontal_list);
                    horizontalList.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
                    horizontalAdapter = new HorizontalAdapter();
                    horizontalList.setAdapter(horizontalAdapter);
                }
            }
    
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                Context context = parent.getContext();
                View itemView = LayoutInflater.from(context).inflate(R.layout.vertical_list_item, parent, false);
                HorizontalListViewHolder holder = new HorizontalListViewHolder(itemView);
                return holder;
            }
    
            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder rawHolder, int position) {
                HorizontalListViewHolder holder = (HorizontalListViewHolder) rawHolder;
                holder.title.setText("Horizontal List No." + position);
                holder.horizontalAdapter.setData(mDataList.get(position));
                holder.horizontalAdapter.setRowIndex(position);
            }
    
            @Override
            public int getItemCount() {
                return mDataList.size();
            }
        }
    
        private static class HorizontalAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
            private List<String> mDataList;
            private int mRowIndex = -1;
            private int[] mColors = new int[]{Color.RED, Color.BLUE, Color.GREEN, Color.MAGENTA, Color.DKGRAY};
    
            public HorizontalAdapter() {
            }
    
            public void setData(List<String> data) {
                if (mDataList != data) {
                    mDataList = data;
                    notifyDataSetChanged();
                }
            }
    
            public void setRowIndex(int index) {
                mRowIndex = index;
            }
    
            private class ItemViewHolder extends RecyclerView.ViewHolder {
    
                private TextView text;
    
                public ItemViewHolder(View itemView) {
                    super(itemView);
                    text = (TextView) itemView.findViewById(R.id.item_text);
                    itemView.setOnClickListener(mItemClickListener);
                }
            }
    
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                Context context = parent.getContext();
                View itemView = LayoutInflater.from(context).inflate(R.layout.horizontal_list_item, parent, false);
                ItemViewHolder holder = new ItemViewHolder(itemView);
                return holder;
            }
    
            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder rawHolder, int position) {
                ItemViewHolder holder = (ItemViewHolder) rawHolder;
                holder.text.setText(mDataList.get(position));
                holder.itemView.setBackgroundColor(mColors[position % mColors.length]);
                holder.itemView.setTag(position);
            }
    
            @Override
            public int getItemCount() {
                return mDataList.size();
            }
    
            private View.OnClickListener mItemClickListener = new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    int columnIndex = (int) v.getTag();
                    int rowIndex = mRowIndex;
    
                    String text = String.format("rowIndex:%d ,columnIndex:%d", rowIndex, columnIndex);
                    showToast(v.getContext(), text);
                    Log.d("test", text);
                }
            };
        }
    
        private static Toast sToast;
    
        public static void showToast(Context context, String text) {
            if (sToast != null) {
                sToast.cancel();
            }
            sToast = Toast.makeText(context, text, Toast.LENGTH_LONG);
            sToast.show();
        }
    }
    

    activity_main.xml

    <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/vertical_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </RelativeLayout>
    

    horizontal_list_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:orientation="vertical">
    
        <TextView
            android:id="@+id/item_text"
            android:textColor="@android:color/white"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:gravity="center_vertical"/>
    
    </LinearLayout>
    

    vertical_list_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <TextView
            android:id="@+id/item_title"
            android:background="@android:color/darker_gray"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:gravity="center_vertical"/>
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/item_horizontal_list"
            android:layout_width="match_parent"
            android:layout_height="50dp"/>
    </LinearLayout>
    

    update at 2015-05-20

    Old Answer:

    I have used TwoWayView to show horizontal lists in a vertical listview just like you. Here is my vertical and horizontal listview adapter code .It works fine for me, all views are reusable, all the rows scroll individually. I hope this may help you.

    public class RecommendAppAdapter extends BaseAdapter implements
        OnItemClickListener {
    public static final String TAG = "RecommendAppAdapter";
    
    //Vertical list data
    private Map<String, List<RecommendApp>> mMapData = new TreeMap<String, List<RecommendApp>>();
    
    private OnItemClickListener mOnItemClickListener = null;
    
    //Vertical list adapter
    public RecommendAppAdapter(Context context) {
    }
    
    @Override
    public boolean isEmpty() {
        return mMapData == null || mMapData.isEmpty();
    }
    
    @Override
    public int getCount() {
        if (!isEmpty()) {
            return mMapData.size();
        } else {
            return 0;
        }
    }
    
    //Get horizental list data
    @Override
    public List<RecommendApp> getItem(int position) {
        if (!isEmpty() && isAvaliablePostion(position)) {
            Iterator<Entry<String, List<RecommendApp>>> entries = mMapData
                    .entrySet().iterator();
            int i = 0;
            Entry<String, List<RecommendApp>> entry = null;
            while (entries.hasNext()) {
                entry = entries.next();
                if (i == position) {
                    return entry.getValue();
                }
                i++;
            }
        }
        return null;
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        ItemAdapter innerListAdapter = null;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = LayoutInflater.from(parent.getContext()).inflate(
                    R.layout.item_recommend_app, null);
            //Some views in vertical list
            holder.type = (TextView) convertView
                    .findViewById(R.id.item_rec_app_type_name);
            //Get horizental list view
            holder.hListView = (TwoWayView) convertView
                    .findViewById(R.id.item_rec_app_list);
            //Bind adapter on horizental list
            innerListAdapter = new ItemAdapter();
            holder.hListView.setAdapter(innerListAdapter);
            //Bind item click listener on horizental list
            holder.hListView.setOnItemClickListener(this);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
            innerListAdapter = (ItemAdapter) holder.hListView.getAdapter();
        }
        //Get horizental list data
        List<RecommendApp> itemList = getItem(position);
    
        holder.type.setText(itemList.get(0).getTypeName());
    
        //Deliver horizental list adapter data
        innerListAdapter.setData(itemList);
    
        return convertView;
    }
    
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        switch (parent.getId()) {
        case R.id.item_rec_app_list:
            if (mOnItemClickListener != null) {
                mOnItemClickListener.onItemClick(parent, view, position, id);
            }
            break;
        }
    }
    
    public void setOnItemClickListener(OnItemClickListener listener) {
        mOnItemClickListener = listener;
    }
    
    public void setData(Map<String, List<RecommendApp>> data) {
        mMapData.clear();
        if (data != null && !data.isEmpty()) {
            mMapData.putAll(data);
        }
        notifyDataSetChanged();
    }
    
    private boolean isAvaliablePostion(int position) {
        if (position >= 0 && position < getCount()) {
            return true;
        } else {
            return false;
        }
    }
    
    
    private class ViewHolder {
        public TextView type;
        public TwoWayView hListView;
    }
    
    //Horizontal list adapter
    //All work are just like the normal use of ListView 
    private class ItemAdapter extends BaseAdapter {
    
        //Horizontal list data
        private List<RecommendApp> mInnerData = null;
    
        private FinalBitmap mFinalBitmap = FinalBitmap.create(MyApp
                .getInstance());
    
        @Override
        public boolean isEmpty() {
            return mInnerData == null || mInnerData.isEmpty();
        }
    
        @Override
        public int getCount() {
            if (!isEmpty()) {
                return mInnerData.size();
            }
            return 0;
        }
    
    
        @Override
        public RecommendApp getItem(int position) {
            if (position >= 0 && position < getCount()) {
                return mInnerData.get(position);
            }
            return null;
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolderInner holder = null;
            if (convertView == null) {
                holder = new ViewHolderInner();
                Context context = parent.getContext();
                convertView = LayoutInflater.from(context).inflate(
                        R.layout.item_recommend_app_inner, null);
                holder.name = (TextView) convertView
                        .findViewById(R.id.item_rec_app_inner_name);
                holder.icon = (ImageView) convertView
                        .findViewById(R.id.item_rec_app_inner_icon);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolderInner) convertView.getTag();
            }
            RecommendApp item = ItemAdapter.this.getItem(position);
            holder.name.setText(item.getAppName());
            mFinalBitmap.display(holder.icon, item.getIcon());
            return convertView;
        }
    
        public void setData(List<RecommendApp> data) {
            mInnerData = data;
            notifyDataSetChanged();
        }
    
        private class ViewHolderInner {
            public TextView name;
            public ImageView icon;
        }
    }
    

    }

    Edit 1

    RecommendApp is a POJO which contain data of an item that shows in the horizontal list.

    item_recommend_app.xml :

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <TextView
        android:id="@+id/item_rec_app_type_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/item_rec_app_type_name_bg"
        android:gravity="left|center_vertical"
        android:paddingBottom="@dimen/item_rec_app_type_name_paddingBottom"
        android:paddingLeft="@dimen/item_rec_app_type_name_paddingLeft"
        android:paddingTop="@dimen/item_rec_app_type_name_paddingTop"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:textIsSelectable="false" />
    
    <com.phonetools.appmanager.widget.TwoWayView
        android:id="@+id/item_rec_app_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" />
    

    item_recommend_app_inner.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="@dimen/item_rec_app_inner_padding" >
    
    <ImageView
        android:id="@+id/item_rec_app_inner_icon"
        android:layout_width="@dimen/item_rec_app_inner_icon_width"
        android:layout_height="@dimen/item_rec_app_inner_icon_height"
        android:layout_gravity="center"
        android:contentDescription="@string/image_desc"
        android:scaleType="fitCenter" />
    
    <TextView
        android:id="@+id/item_rec_app_inner_name"
        android:layout_width="@dimen/item_rec_app_inner_icon_width"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:ellipsize="end"
        android:gravity="center"
        android:singleLine="true"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:textIsSelectable="false" />
    

    RecommendApp.java:

    public class RecommendApp {
    public RecommendApp() {
    }
    
    
    private String packageName;
    
    
    private String appName;
    
    
    private String versionName;
    
    
    private float size;
    
    
    private String icon;
    
    
    private int typeId;
    
    
    private String typeName;
    
    
    private String installLink;
    
    
    private String description;
    
    
    private float ratingScore;
    
    
    private long installedSum;
    

    }