Programmatically Rendering Custom List Rows with Icons in Android ListActivity

Integrating graphical elements into a ListActivity requires precise handling of layout construction and data binding. While modern development often favors XML-based layouts, programmatic generation offers flexibility when strict resource constraints exist. Below is an approach to building list rows entirely within Java code, embedding checkboxes, application names, status flags, and icons.

Defining the Custom Row Component

To create a distinct look for each list entry, we define a class etxending LinearLayout. Instead of loading an XML resource, individual widgets are instantiated and appended using addView. This allows dynamic control over orientation and dimensions at runtime.

public class AppListItem extends LinearLayout {
    private CheckBox checkBox;
    private ImageView appIcon;
    private TextView appNameText;
    private TextView sysFlagText;

    public AppListItem(Context context) {
        super(context);
        setOrientation(HORIZONTAL);

        // Setup Checkbox column
        checkBox = new CheckBox(context);
        addView(checkBox, generateLayoutParams(0.2f, 60));

        // Setup Icon column
        appIcon = new ImageView(context);
        addView(appIcon, generateLayoutParams(0.2f, 60));

        // Setup Name column
        appNameText = new TextView(context);
        addView(appNameText, generateLayoutParams(0.4f, 60));

        // Setup Flag column
        sysFlagText = new TextView(context);
        addView(sysFlagText, generateLayoutParams(0.2f, 60));
    }

    private static LayoutParams generateLayoutParams(float weight, int height) {
        return new LayoutParams((int)(weight * getScreenWidth()), height);
    }

    private static int getScreenWidth() {
        WindowManager wm = (WindowManager) ((Activity)getContext()).getWindowManager();
        return wm.getDefaultDisplay().getWidth();
    }

    public void setIcon(Drawable drawable) {
        appIcon.setImageDrawable(drawable);
    }

    public void setTextFields(String name, String flag) {
        appNameText.setText(name);
        sysFlagText.setText(flag);
    }
}

Constructing the Data Adapter

The adapter bridges the gap between the dataset and the ListActivity. By extending BaseAdapter, we gain full control over how cells are recycled and populated. We maintain a dedicatde data model rather than storing raw view instances to ensure stability.

public class InstalledAppsAdapter extends BaseAdapter {
    private final Context context;
    private final List<apkinfo> dataPool;

    public InstalledAppsAdapter(Context context) {
        this.context = context;
        this.dataPool = new ArrayList<>();
    }

    @Override
    public int getCount() {
        return dataPool.size();
    }

    @Override
    public Object getItem(int position) {
        return dataPool.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        AppListItem view;

        if (convertView == null) {
            view = new AppListItem(context);
        } else {
            view = (AppListItem) convertView;
        }

        ApkInfo info = dataPool.get(position);
        view.setIcon(info.icon);
        view.setTextFields(info.label, info.isSystem ? "Yes" : "No");

        return view;
    }

    public void addItem(ApkInfo item) {
        dataPool.add(item);
        notifyDataSetChanged();
    }
}

// Supporting Data Model
class ApkInfo {
    Drawable icon;
    String label;
    boolean isSystem;

    public ApkInfo(Drawable icon, String label, boolean isSystem) {
        this.icon = icon;
        this.label = label;
        this.isSystem = isSystem;
    }
}</apkinfo>

Implementing the Activity Logic

The activity itself manages the lifecycle of the data retrieval process. Rather than using setContentView for a static layout, we utilize setListAdapter. Inside onCreate, we query the system for installed packages, filter their properties, and feed them into the adapter.

public class PackageManagerList extends ListActivity {

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

        PackageManager pm = getPackageManager();
        List<packageinfo> installedPkgs = pm.getInstalledPackages(PackageManager.GET_META_DATA);

        InstalledAppsAdapter adapter = new InstalledAppsAdapter(this);

        for (PackageInfo pkg : installedPkgs) {
            ApplicationInfo appInfo = pkg.applicationInfo;
            String pkgLabel = (String) appInfo.loadLabel(pm);
            
            // Determine System Status
            boolean isSystemApp = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;

            // Retrieve or Default Icon
            Drawable appIcon = null;
            try {
                appIcon = appInfo.loadIcon(pm);
                if (appIcon == null) {
                    appIcon = getResources().getDrawable(R.drawable.ic_launcher);
                }
            } catch (Exception e) {
                appIcon = getResources().getDrawable(R.drawable.ic_launcher);
            }

            adapter.addItem(new ApkInfo(appIcon, pkgLabel, isSystemApp));
        }

        setListAdapter(adapter);
    }
}</packageinfo>

Tags: android-development listactivity baseadapter packagemanager programmatic-ui

Posted on Mon, 22 Jun 2026 18:11:15 +0000 by mark_nsx