master a5e3e4e50e05 cached
24 files
36.9 KB
8.8k tokens
38 symbols
1 requests
Download .txt
Repository: umano/MultiItemRowListAdapter
Branch: master
Commit: a5e3e4e50e05
Files: 24
Total size: 36.9 KB

Directory structure:
gitextract__27co_bj/

├── .gitignore
├── AndroidManifest.xml
├── LICENSE.txt
├── README.md
├── libs/
│   └── android-support-v4.jar
├── proguard-project.txt
├── project.properties
├── res/
│   ├── drawable/
│   │   ├── above_shadow.xml
│   │   └── list_selector.xml
│   ├── layout/
│   │   ├── activity_demo.xml
│   │   └── include_list_item_contact.xml
│   ├── menu/
│   │   └── demo.xml
│   ├── values/
│   │   ├── colors.xml
│   │   ├── dimen.xml
│   │   ├── integers.xml
│   │   ├── strings.xml
│   │   └── styles.xml
│   ├── values-land/
│   │   └── integers.xml
│   ├── values-sw600dp/
│   │   └── integers.xml
│   ├── values-sw600dp-land/
│   │   └── integers.xml
│   ├── values-v11/
│   │   └── styles.xml
│   └── values-v14/
│       └── styles.xml
└── src/
    └── com/
        └── sothree/
            └── multiitemrowlistadapter/
                ├── MultiItemRowListAdapter.java
                └── demo/
                    └── DemoActivity.java

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# built application files
*.apk
*.ap_

# files for the dex VM
*.dex

# Java class files
*.class

# generated files
bin/
gen/

# Local configuration file (sdk path, etc)
local.properties

# Eclipse project files
.classpath
.project


================================================
FILE: AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sothree.slidinguppaneldemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="17" />
    
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />   

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.sothree.multiitemrowlistadapter.demo.DemoActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
    </application>

</manifest>


================================================
FILE: LICENSE.txt
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
Android Multi Item Row ListAdapter
==================================
### An Easy Way to Make Your ListView's Look Amazing on Tablets
With the launch of the tablet version of [Umano](http://umanoapp.com) [Android app](https://play.google.com/store/apps/details?id=com.sothree.umano) we decided to open-source another component, which allows you to very quickly make your ListViews and ListActivities look amazing on tablets and phablets by placing multiple items of your ListAdapter in each row in a ListView. Umano Team <3 Open Source.

As seen in Umano ([http://umanoapp.com](http://umanoapp.com)):

![MultiItemRowListAdapter](https://raw.github.com/umano/MultiItemRowListAdapter/master/multiitem.png)

### Usage

All you need to do is wrap your original ListAdapter using a `MultiItemRowListAdapter`:
```java
    int spacing = (int)getResources().getDimension(R.dimen.spacing);
    int itemsPerRow = getResources().getInteger(R.integer.items_per_row);
    MultiItemRowListAdapter wrapperAdapter = new MultiItemRowListAdapter(this, mAdapter, itemsPerRow, spacing);
    setListAdapter(wrapperAdapter);
```
As you can see the constructor for `MultiItemRowListAdapter` takes two parameters `itemsPerRow` and `spacing`. The first one is just the number of items from the original adapter to place on each row, and the second one is the cell spacing in pixels between the items. It's really convenient to specify the parameters in xml, so that you can easily vary the number of items per row on different screen orientations and sizes.
res/values/integers.xml - phone portrait (1 items per row)
```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="items_per_row">1</integer>
</resources>
```
res/values-land/integers.xml - phone landscape (2 items per row)
```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="items_per_row">2</integer>
</resources>
```
res/values-sw600/integers.xml - 7' tablet portrait (2 items per row)
```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="items_per_row">2</integer>
</resources>
```
res/values-sw600-land/integers.xml - 7' tablet landscape (3 items per row)
```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="items_per_row">3</integer>
</resources>
```

### Requrements
Tested on Android 2.2+

### Licence
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this work except in compliance with the License.
You may obtain a copy of the License in the LICENSE file, or at:

  [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


================================================
FILE: proguard-project.txt
================================================
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}


================================================
FILE: project.properties
================================================
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt

# Project target.
target=android-17


================================================
FILE: res/drawable/above_shadow.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">   
    <gradient
        android:startColor="#20000000"
        android:endColor="@android:color/transparent"
        android:angle="90" >
    </gradient>
</shape>

================================================
FILE: res/drawable/list_selector.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_pressed="true"
          android:drawable="@color/app_background" /> <!-- pressed -->
    <item android:drawable="@android:color/white" /> <!-- default -->
</selector>

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

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:divider="@null"        
        android:listSelector="@color/app_background"
        android:dividerHeight="0dp"       
        android:layout_weight="1"/>

    <TextView
        android:id="@android:id/empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
		android:textSize="16sp"
        android:text="Loading..." />

</LinearLayout>

================================================
FILE: res/layout/include_list_item_contact.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:baselineAligned="false"
    android:orientation="horizontal" 
    android:gravity="center_vertical"
    android:focusable="false"
    android:focusableInTouchMode="false"
    android:duplicateParentState="false"
    android:clickable="true" 
    android:background="@drawable/list_selector">

    <ImageView
        android:id="@+id/image"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="top"
        android:layout_margin="10dp"
        android:background="@drawable/ic_launcher"
        android:contentDescription="@string/app_name"
        android:scaleType="centerCrop"/>

    <TextView
        android:id="@+id/lbl_contact_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:layout_marginRight="10dp"
        android:ellipsize="end"
        android:singleLine="true"
        android:duplicateParentState="true"
        android:textIsSelectable="false"
        android:textSize="16sp" />   

</LinearLayout>

================================================
FILE: res/menu/demo.xml
================================================
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/action_settings"/>

</menu>


================================================
FILE: res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="app_background">#e8e8e8</color>    
</resources>


================================================
FILE: res/values/dimen.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="spacing">10dp</dimen>   
</resources>

================================================
FILE: res/values/integers.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="items_per_row">1</integer>
</resources>

================================================
FILE: res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">MultiItemRowListAdapter Demo</string>
    <string name="action_settings">Settings</string>
    <string name="hello_world">Hello world!</string>

</resources>


================================================
FILE: res/values/styles.xml
================================================
<resources>

    <!--
        Base application theme, dependent on API level. This theme is replaced
        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
    -->
    <style name="AppBaseTheme" parent="android:Theme.Light">
        <!--
            Theme customizations available in newer API levels can go in
            res/values-vXX/styles.xml, while customizations related to
            backward-compatibility can go here.
        -->
		<item name="android:windowActionBarOverlay">true</item>        
    </style>

    <!-- Application theme. -->
    <style name="AppTheme" parent="AppBaseTheme">
        <item name="android:windowBackground">@color/app_background</item>
    </style>

</resources>


================================================
FILE: res/values-land/integers.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="items_per_row">2</integer>
</resources>

================================================
FILE: res/values-sw600dp/integers.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="items_per_row">2</integer>
</resources>

================================================
FILE: res/values-sw600dp-land/integers.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="items_per_row">3</integer>
</resources>

================================================
FILE: res/values-v11/styles.xml
================================================
<resources>

    <!--
        Base application theme for API 11+. This theme completely replaces
        AppBaseTheme from res/values/styles.xml on API 11+ devices.
    -->
    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
        <!-- API 11 theme customizations can go here. -->
    </style>

</resources>


================================================
FILE: res/values-v14/styles.xml
================================================
<resources>

    <!--
        Base application theme for API 14+. This theme completely replaces
        AppBaseTheme from BOTH res/values/styles.xml and
        res/values-v11/styles.xml on API 14+ devices.
    -->
    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
        <!-- API 14 theme customizations can go here. -->
    </style>

</resources>


================================================
FILE: src/com/sothree/multiitemrowlistadapter/MultiItemRowListAdapter.java
================================================
package com.sothree.multiitemrowlistadapter;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.WrapperListAdapter;

public class MultiItemRowListAdapter implements WrapperListAdapter {
    private final ListAdapter mAdapter;
    private final int mItemsPerRow;
    private final int mCellSpacing;
    private final WeakReference<Context> mContextReference;
    private final LinearLayout.LayoutParams mItemLayoutParams;
    private final AbsListView.LayoutParams mRowLayoutParams;

    public MultiItemRowListAdapter(Context context, ListAdapter adapter, int itemsPerRow, int cellSpacing) {
        if (itemsPerRow <= 0) {
            throw new IllegalArgumentException("Number of items per row must be positive");
        }
        mContextReference = new WeakReference<Context>(context);
        mAdapter = adapter;
        mItemsPerRow = itemsPerRow;
        mCellSpacing = cellSpacing;

        mItemLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT);
        mItemLayoutParams.setMargins(cellSpacing, cellSpacing, 0, 0);
        mItemLayoutParams.weight = 1;
        mRowLayoutParams = new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    }

    @Override
    public boolean isEmpty() {
        return (mAdapter == null || mAdapter.isEmpty());
    }

    public int getItemsPerRow() {
        return mItemsPerRow;
    }

    @Override
    public int getCount() {
        if (mAdapter != null) {
            return (int)Math.ceil(1.0f * mAdapter.getCount() / mItemsPerRow);
        }
        return 0;
    }

    @Override
    public boolean areAllItemsEnabled() {
        if (mAdapter != null) {
            return mAdapter.areAllItemsEnabled();
        } else {
            return true;
        }
    }

    @Override
    public boolean isEnabled(int position) {
        if (mAdapter != null) {
            // the cell is enabled if at least one item is enabled
            boolean enabled = false;
            for (int i = 0; i < mItemsPerRow; ++i) {
                int p = position * mItemsPerRow + i;
                if (p < mAdapter.getCount()) {
                    enabled |= mAdapter.isEnabled(p);
                }
            }
            return enabled;
        }
        return true;
    }

    @Override
    public Object getItem(int position) {
        if (mAdapter != null) {
            List<Object> items = new ArrayList<Object>(mItemsPerRow);
            for (int i = 0; i < mItemsPerRow; ++i) {
                int p = position * mItemsPerRow + i;
                if (p < mAdapter.getCount()) {
                    items.add(mAdapter.getItem(p));
                }
            }
            return items;
        }
        return null;
    }

    @Override
    public long getItemId(int position) {
        if (mAdapter != null) {
            return position;
        }
        return -1;
    }

    @Override
    public boolean hasStableIds() {
        if (mAdapter != null) {
            return mAdapter.hasStableIds();
        }
        return false;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Context c = mContextReference.get();
        if (c == null || mAdapter == null) return null;

        LinearLayout view = null;
        if (convertView == null
                || !(convertView instanceof LinearLayout)
                || !((Integer)convertView.getTag()).equals(mItemsPerRow)) {
            // create a linear Layout
            view = new LinearLayout(c);
            view.setPadding(0, 0, mCellSpacing, 0);
            view.setLayoutParams(mRowLayoutParams);
            view.setOrientation(LinearLayout.HORIZONTAL);
            view.setBaselineAligned(false);
            view.setTag(Integer.valueOf(mItemsPerRow));
        } else {
            view = (LinearLayout) convertView;
        }

        for (int i = 0; i < mItemsPerRow; ++i) {
            View subView = i < view.getChildCount() ? view.getChildAt(i) : null;
            int p = position * mItemsPerRow + i;

            View newView = subView;
            if (p < mAdapter.getCount()) {
            	if (subView instanceof PlaceholderView){
            		view.removeView(subView);
            		subView = null;
            	}
                newView = mAdapter.getView(p, subView, view);
            } else if (subView == null || !(subView instanceof PlaceholderView)) {
                newView = new PlaceholderView(c);
            }
            if (newView != subView || i >= view.getChildCount()) {
                if (i < view.getChildCount()) {
                    view.removeView(subView);
                }
                newView.setLayoutParams(mItemLayoutParams);
                view.addView(newView, i);
            }
        }

        return view;
    }

    @Override
    public int getItemViewType(int position) {
        if (mAdapter != null) {
            return mAdapter.getItemViewType(position);
        }

        return -1;
    }

    @Override
    public int getViewTypeCount() {
        if (mAdapter != null) {
            return mAdapter.getViewTypeCount();
        }
        return 1;
    }

    @Override
    public void registerDataSetObserver(DataSetObserver observer) {
        if (mAdapter != null) {
            mAdapter.registerDataSetObserver(observer);
        }
    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {
        if (mAdapter != null) {
            mAdapter.unregisterDataSetObserver(observer);
        }
    }

    @Override
    public ListAdapter getWrappedAdapter() {
        return mAdapter;
    }

    public static class PlaceholderView extends View {

        public PlaceholderView(Context context) {
            super(context);
        }

    }
}

================================================
FILE: src/com/sothree/multiitemrowlistadapter/demo/DemoActivity.java
================================================
package com.sothree.multiitemrowlistadapter.demo;

import com.sothree.multiitemrowlistadapter.MultiItemRowListAdapter;
import com.sothree.slidinguppaneldemo.R;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Contacts.Photo;
import android.app.Activity;
import android.app.ListActivity;
import android.app.LoaderManager;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class DemoActivity extends ListActivity implements
LoaderManager.LoaderCallbacks<Cursor>,
OnItemClickListener {

    private ContactsAdapter mContactsAdapter;

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

        mContactsAdapter = new ContactsAdapter(this);
        // We need to set the item on click listener on the original adapter instead of the ListView
        // since each row in MultiItemRowListAdapter has multiple items
        mContactsAdapter.setOnItemClickListener(this);

        int spacing = (int)getResources().getDimension(R.dimen.spacing);
        int itemsPerRow = getResources().getInteger(R.integer.items_per_row);
        MultiItemRowListAdapter wrapperAdapter = new MultiItemRowListAdapter(this, mContactsAdapter, itemsPerRow, spacing);
        setListAdapter(wrapperAdapter);
        getLoaderManager().restartLoader(0, null, this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mContactsAdapter.close();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.demo, menu);
        return true;
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
        Uri uri = ContactsContract.Contacts.CONTENT_URI;
        String[] projection;
        String displayNameColumn;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            displayNameColumn = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY;
            projection= new String[] {
                ContactsContract.Contacts._ID,
                ContactsContract.Contacts.PHOTO_THUMBNAIL_URI,
                displayNameColumn
            };

        } else {
            displayNameColumn = ContactsContract.Contacts.DISPLAY_NAME;
            projection= new String[] {
                ContactsContract.Contacts._ID,
                displayNameColumn
            };
        }
        String selection =
                ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '1' AND " +
                "NULLIF(" + displayNameColumn +", '') IS NOT NULL";
        String[] selectionArgs = null;
        String sortOrder = ContactsContract.Contacts.STARRED + " DESC, " + ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
        return new CursorLoader(DemoActivity.this, uri, projection, selection, selectionArgs, sortOrder);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        mContactsAdapter.swapCursor(cursor);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> arg0) {
        mContactsAdapter.swapCursor(null);
    }
    private static class ContactsAdapter extends BaseAdapter {
        private final LayoutInflater mInflater;

        private Cursor mCursor;
        private OnItemClickListener mOnItemClickListener;

        private final class ContactViewHolder {
            public TextView mContactName;
            public ImageView mContactImage;
        }

        public ContactsAdapter(Activity context) {
            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

        public void setOnItemClickListener(OnItemClickListener listener) {
            mOnItemClickListener = listener;
        }

        public Cursor swapCursor(Cursor newCursor) {
            if (mCursor != null && mCursor != newCursor) {
                mCursor.close();
            }
            mCursor = newCursor;
            notifyDataSetChanged();
            return mCursor;
        }

        @Override
        public int getCount() {
            if (mCursor != null && mCursor.getCount() > 0) {
                return mCursor.getCount();
            }
            return 0;
        }

        @Override
        public Object getItem(int position) {
            if (mCursor != null) {
                mCursor.moveToPosition(position);
                return mCursor;
            }
            return null;
        }

        //return the header view, if it's in a section header position
        @Override
        public View getView(final int position, View convertView, final ViewGroup parent) {
            ContactViewHolder viewHolder;
            if (convertView == null || !(convertView.getTag() instanceof ContactViewHolder)) {
                convertView = mInflater.inflate(R.layout.include_list_item_contact, parent, false);
                viewHolder = new ContactViewHolder();
                viewHolder.mContactName = (TextView) convertView.findViewById(R.id.lbl_contact_name);
                viewHolder.mContactImage = (ImageView) convertView.findViewById(R.id.image);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ContactViewHolder)convertView.getTag();
            }
            Cursor c = (Cursor)getItem(position);

            if (c == null) {
                return convertView;
            }

            int photoColumnIndex;
            int nameColumnIndex;

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                photoColumnIndex = mCursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI);
                nameColumnIndex = mCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY);
            } else {
                photoColumnIndex = mCursor.getColumnIndex(ContactsContract.Contacts._ID);;
                nameColumnIndex = mCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
            }

            if (nameColumnIndex >= 0) {
                viewHolder.mContactName.setText(c.getString(nameColumnIndex));
            }

            if (photoColumnIndex >= 0 && c.getString(photoColumnIndex) != null) {
                String photoData = c.getString(photoColumnIndex);
                // Creates a holder for the URI.
                Uri thumbUri;
                // If Android 3.0 or later
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                    // Sets the URI from the incoming PHOTO_THUMBNAIL_URI
                    thumbUri = Uri.parse(photoData);
                } else {
                    // Prior to Android 3.0, constructs a photo Uri using _ID
                    /*
                     * Creates a contact URI from the Contacts content URI
                     * incoming photoData (_ID)
                     */
                    final Uri contactUri = Uri.withAppendedPath(
                            Contacts.CONTENT_URI, photoData);
                    /*
                     * Creates a photo URI by appending the content URI of
                     * Contacts.Photo.
                     */
                    thumbUri =
                            Uri.withAppendedPath(
                                    contactUri, Photo.CONTENT_DIRECTORY);
                }
                viewHolder.mContactImage.setImageURI(thumbUri);
            } else {
                viewHolder.mContactImage.setImageResource(0);
            }

            final View clickedView = convertView;
            // set the on click listener for each of the items
            convertView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mOnItemClickListener != null) {
                        mOnItemClickListener.onItemClick(null, clickedView, position, position);
                    }

                }
            });

            return convertView;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public boolean areAllItemsEnabled() {
            return false;
        }

        @Override
        public boolean isEnabled(int position) {
            return true;
        }

        public void close() {
            if (mCursor != null) {
                mCursor.close();
            }
        }
    }
    @Override
    public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
        if (mContactsAdapter != null) {
            Cursor c = (Cursor)mContactsAdapter.getItem(position);
            String name = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
            Toast t = Toast.makeText(this, "Clicked " + name, Toast.LENGTH_SHORT);
            t.show();
        }
    }
}
Download .txt
gitextract__27co_bj/

├── .gitignore
├── AndroidManifest.xml
├── LICENSE.txt
├── README.md
├── libs/
│   └── android-support-v4.jar
├── proguard-project.txt
├── project.properties
├── res/
│   ├── drawable/
│   │   ├── above_shadow.xml
│   │   └── list_selector.xml
│   ├── layout/
│   │   ├── activity_demo.xml
│   │   └── include_list_item_contact.xml
│   ├── menu/
│   │   └── demo.xml
│   ├── values/
│   │   ├── colors.xml
│   │   ├── dimen.xml
│   │   ├── integers.xml
│   │   ├── strings.xml
│   │   └── styles.xml
│   ├── values-land/
│   │   └── integers.xml
│   ├── values-sw600dp/
│   │   └── integers.xml
│   ├── values-sw600dp-land/
│   │   └── integers.xml
│   ├── values-v11/
│   │   └── styles.xml
│   └── values-v14/
│       └── styles.xml
└── src/
    └── com/
        └── sothree/
            └── multiitemrowlistadapter/
                ├── MultiItemRowListAdapter.java
                └── demo/
                    └── DemoActivity.java
Download .txt
SYMBOL INDEX (38 symbols across 2 files)

FILE: src/com/sothree/multiitemrowlistadapter/MultiItemRowListAdapter.java
  class MultiItemRowListAdapter (line 17) | public class MultiItemRowListAdapter implements WrapperListAdapter {
    method MultiItemRowListAdapter (line 25) | public MultiItemRowListAdapter(Context context, ListAdapter adapter, i...
    method isEmpty (line 40) | @Override
    method getItemsPerRow (line 45) | public int getItemsPerRow() {
    method getCount (line 49) | @Override
    method areAllItemsEnabled (line 57) | @Override
    method isEnabled (line 66) | @Override
    method getItem (line 82) | @Override
    method getItemId (line 97) | @Override
    method hasStableIds (line 105) | @Override
    method getView (line 113) | @Override
    method getItemViewType (line 159) | @Override
    method getViewTypeCount (line 168) | @Override
    method registerDataSetObserver (line 176) | @Override
    method unregisterDataSetObserver (line 183) | @Override
    method getWrappedAdapter (line 190) | @Override
    class PlaceholderView (line 195) | public static class PlaceholderView extends View {
      method PlaceholderView (line 197) | public PlaceholderView(Context context) {

FILE: src/com/sothree/multiitemrowlistadapter/demo/DemoActivity.java
  class DemoActivity (line 30) | public class DemoActivity extends ListActivity implements
    method onCreate (line 36) | @Override
    method onDestroy (line 53) | @Override
    method onCreateOptionsMenu (line 59) | @Override
    method onCreateLoader (line 66) | @Override
    method onLoadFinished (line 94) | @Override
    method onLoaderReset (line 99) | @Override
    class ContactsAdapter (line 103) | private static class ContactsAdapter extends BaseAdapter {
      class ContactViewHolder (line 109) | private final class ContactViewHolder {
      method ContactsAdapter (line 114) | public ContactsAdapter(Activity context) {
      method setOnItemClickListener (line 118) | public void setOnItemClickListener(OnItemClickListener listener) {
      method swapCursor (line 122) | public Cursor swapCursor(Cursor newCursor) {
      method getCount (line 131) | @Override
      method getItem (line 139) | @Override
      method getView (line 149) | @Override
      method getItemId (line 226) | @Override
      method areAllItemsEnabled (line 231) | @Override
      method isEnabled (line 236) | @Override
      method close (line 241) | public void close() {
    method onItemClick (line 247) | @Override
Condensed preview — 24 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (40K chars).
[
  {
    "path": ".gitignore",
    "chars": 231,
    "preview": "# built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\n"
  },
  {
    "path": "AndroidManifest.xml",
    "chars": 1047,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "LICENSE.txt",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 2931,
    "preview": "Android Multi Item Row ListAdapter\n==================================\n### An Easy Way to Make Your ListView's Look Amazi"
  },
  {
    "path": "proguard-project.txt",
    "chars": 781,
    "preview": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in t"
  },
  {
    "path": "project.properties",
    "chars": 563,
    "preview": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# T"
  },
  {
    "path": "res/drawable/above_shadow.xml",
    "chars": 269,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">   \n    <gradie"
  },
  {
    "path": "res/drawable/list_selector.xml",
    "chars": 301,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n    <item "
  },
  {
    "path": "res/layout/activity_demo.xml",
    "chars": 751,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    and"
  },
  {
    "path": "res/layout/include_list_item_contact.xml",
    "chars": 1306,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andr"
  },
  {
    "path": "res/menu/demo.xml",
    "chars": 254,
    "preview": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <item\n        android:id=\"@+id/action_settings\"\n"
  },
  {
    "path": "res/values/colors.xml",
    "chars": 117,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"app_background\">#e8e8e8</color>    \n</resources>\n"
  },
  {
    "path": "res/values/dimen.xml",
    "chars": 105,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"spacing\">10dp</dimen>   \n</resources>"
  },
  {
    "path": "res/values/integers.xml",
    "chars": 109,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <integer name=\"items_per_row\">1</integer>\n</resources>"
  },
  {
    "path": "res/values/strings.xml",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">MultiItemRowListAdapter Demo</string>\n  "
  },
  {
    "path": "res/values/styles.xml",
    "chars": 724,
    "preview": "<resources>\n\n    <!--\n        Base application theme, dependent on API level. This theme is replaced\n        by AppBaseT"
  },
  {
    "path": "res/values-land/integers.xml",
    "chars": 109,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <integer name=\"items_per_row\">2</integer>\n</resources>"
  },
  {
    "path": "res/values-sw600dp/integers.xml",
    "chars": 109,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <integer name=\"items_per_row\">2</integer>\n</resources>"
  },
  {
    "path": "res/values-sw600dp-land/integers.xml",
    "chars": 109,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <integer name=\"items_per_row\">3</integer>\n</resources>"
  },
  {
    "path": "res/values-v11/styles.xml",
    "chars": 324,
    "preview": "<resources>\n\n    <!--\n        Base application theme for API 11+. This theme completely replaces\n        AppBaseTheme fr"
  },
  {
    "path": "res/values-v14/styles.xml",
    "chars": 381,
    "preview": "<resources>\n\n    <!--\n        Base application theme for API 14+. This theme completely replaces\n        AppBaseTheme fr"
  },
  {
    "path": "src/com/sothree/multiitemrowlistadapter/MultiItemRowListAdapter.java",
    "chars": 6106,
    "preview": "package com.sothree.multiitemrowlistadapter;\n\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport jav"
  },
  {
    "path": "src/com/sothree/multiitemrowlistadapter/demo/DemoActivity.java",
    "chars": 9586,
    "preview": "package com.sothree.multiitemrowlistadapter.demo;\n\nimport com.sothree.multiitemrowlistadapter.MultiItemRowListAdapter;\ni"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the umano/MultiItemRowListAdapter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 24 files (36.9 KB), approximately 8.8k tokens, and a symbol index with 38 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!