52

I'm doing an application for Android and something I need is that it shows a list of all files and directories in the SD Card and it has to be able to move through the different directories. I found a good tutorial in anddev. I modified a few things so the application moves in the SD Card and not in Android root Directories but the rest is mostly the same.

This is my xml file for the activity:

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@id/android:list"       
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
</ListView>

And this is the code for the Activity:

import hackreatorz.cifrador.R;
import java.io.File;
import java.util.ArrayList;
import android.app.ListActivity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class ArchivosSD extends ListActivity {

    private ArrayList<String> directoryEntries = new ArrayList<String>();
    private File currentDirectory = new File("/sdcard/");

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

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }

    private void browseToSD() {
        browseTo(new File("/sdcard/"));
    }

    private void upOneLevel() {
         if(this.currentDirectory.getParent() != null)
             this.browseTo(this.currentDirectory.getParentFile());
    }

    private void browseTo(final File directory) {
        if (directory.isDirectory()) {
                this.currentDirectory = directory;
                fill(directory.listFiles());
        } else {
                Toast.makeText(ArchivosSD.this, this.directoryEntries.get(this.getSelectedItemPosition()), Toast.LENGTH_SHORT).show();
                }
    }

    private void fill(File[] files) {
        this.directoryEntries.clear();
        this.directoryEntries.add(getString(R.string.current_dir));
        if(this.currentDirectory.getParent() != null)
            this.directoryEntries.add(getString(R.string.up_one_level));
            int currentPathStringLength = (int) this.currentDirectory.getAbsoluteFile().length();
            for (File file : files) {
                this.directoryEntries.add(file.getAbsolutePath().substring(currentPathStringLength));           
            }
        setListAdapter(new ArrayAdapter<String>(this, R.layout.archivos_sd, this.directoryEntries));
    }   

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        int selectionRowID = (int) this.getSelectedItemPosition();
        String selectedFileString = this.directoryEntries.get(selectionRowID);
        if (selectedFileString.equals(".")) {
            this.browseToSD();
        } else if(selectedFileString.equals("..")) {
            this.upOneLevel();
        } else {
            File clickedFile = null;
            clickedFile = new File(this.currentDirectory.getAbsolutePath() + this.directoryEntries.get(selectionRowID));
            if(clickedFile != null)
                this.browseTo(clickedFile);
            }
    }
}

I don't get any errors in Eclipse, but I get a Force Close when running the application on my phone and when I look at Logcat I see the following:

01-01 23:30:29.858: ERROR/AndroidRuntime(14911): FATAL EXCEPTION: main 01-01 23:30:29.858: ERROR/AndroidRuntime(14911): java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView

I don't have a clue what to do, I've looked up in Google and I didn't find anything and I did the same at stackoverflow. This is my first aplication in Java and for Android so I'm a real n00b and if the answer was there, I didn't understand it so I would really appreciate if you could explain what I should do to fix this error and why.

Thanks for everything in advance.

Kabir
  • 852
  • 7
  • 11
Alberto Elias
  • 891
  • 2
  • 8
  • 17

6 Answers6

90

For others who have this problem and have inflated a layout in ArrayAdapter's getView, set the parent parameter to null, as in view = inflater.inflate(R.layout.mylayout, null);

Rose Perrone
  • 61,572
  • 58
  • 208
  • 243
  • 3
    Great comment! I've struggling for hours because of this... Anyone know why? – danielrvt-sgb Aug 11 '13 at 03:30
  • This also fixed my problem. I think it's something to do with listview parent requiring layout params. – twigg Aug 25 '13 at 21:09
  • 13
    This is definitely a wrong answer, you should always pas a parent viewgroup while inflating, there are some exceptions but this is not one of them, for more info see here: http://www.doubleencore.com/2013/05/layout-inflation-as-intended/. The general problem that people are having is a wrongly configured xml. – Tobrun Apr 23 '14 at 17:09
  • 35
    This is a solution, but you are using it wrong :). Passing parent as null instantiates the object without a proper container and should be readjusted later, which is time consuming and affects your performance. Instead, use inflate (R.layout.mylayout, parent,false). So what you are saying is use the parent to measure but do not attach to it, as it will be done after returning the view – Sergi and Replace Jul 28 '14 at 08:58
86

Rule of thumb

You should always try to pass in a parent while inflating a View, else the framework will not be able to identify what layout params to use for that inflated view.

The correct way of handling the exception is by passing false as third parameter, this flag indicates if a view should be added directly to the passed container or not.

In the case of an adapter, the adapter itself will add the view after you returned the view from you getview method:

view = inflater.inflate(R.layout.mylayout, parent, **false**);

BaseAdapter sample, comes from just-for-us DevBytes:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View result = convertView;
    if (result == null) {
        result = mInflater.inflate(R.layout.grid_item, parent, false);
    }

    // Try to get view cache or create a new one if needed
    ViewCache viewCache = (ViewCache) result.getTag();
    if (viewCache == null) {
        viewCache = new ViewCache(result);
        result.setTag(viewCache);
    }

    // Fetch item
    Coupon coupon = getItem(position);

    // Bind the data
    viewCache.mTitleView.setText(coupon.mTitle);
    viewCache.mSubtitleView.setText(coupon.mSubtitle);
    viewCache.mImageView.setImageURI(coupon.mImageUri);

    return result;
}

CursorAdapter sample:

@Override
public View newView(final Context context, final Cursor cursor, final ViewGroup root)          {
    return mInflater.inflate(R.layout.list_item_ticket, root, false);
}

More information about view inflation can be found in this article.

Minh Nghĩa
  • 854
  • 1
  • 11
  • 28
Tobrun
  • 18,291
  • 10
  • 66
  • 81
  • ViewCache does not exist as a class, and I have found this same code in many forums, is it a copy paste? Have you ever tried to make it compile? – Windgate Aug 11 '21 at 11:01
15

When creating an ArrayAdapter like yours, the ArrayAdapter needs a layout resource containing just a TextView. Try changing your ArrayAdapter creation with:

setListAdapter(new ArrayAdapter<String>(this, 
      android.R.layout.simple_list_item_1, 
      this.directoryEntries));

and see if it works.

Derlin
  • 9,572
  • 2
  • 32
  • 53
Ander Webbs
  • 1,158
  • 1
  • 8
  • 11
  • Great! It works thank you so much! I have a doubt about your answer, what does "android.R.layout.simple_list_item_1" include? If I use "android.R.layout.simple_list_item_1", what happens with the layout I did with a Listview and a textview? Thanks a lot! – Alberto Elias Jan 02 '11 at 09:32
  • When you create an ArrayAdapter you pass a layout resource to be used on each list item (and ArrayAdapter only wants a TextView). The layout file where you setup the "screen" is meant to be used inside onCreate, with setContentView(R.layout.your_layout). – Ander Webbs Jan 02 '11 at 09:54
  • COuld I change "android.R.layout.simple_list_item_1" to R.layout.myxml if that xml file had the following code?: – Alberto Elias Jan 02 '11 at 09:57
  • I'm not sure if I get it. So, in OnCreate a should have setContentView(R.layout.my_layout with only a TextView and then on the adapter i have to pass the layout resource android.R.layout.simple_list_item_1? – Alberto Elias Jan 02 '11 at 13:15
  • Your previous comment is right, you can pass your own layout to the ArrayAdapter constructor. But your second comment is wrong, on setContentView you pass tge whole activity initial layout (headers, buttons, the ListView, etc). Although on ListActivities there's already a default layout with the ListView. – Ander Webbs Jan 02 '11 at 13:29
  • So if I want to do a ListView, I don't have to creat my own layout? I just pass the default layout? – Alberto Elias Jan 02 '11 at 20:19
  • If you want to create your own layout for the whole screen, you can pass it to the setContentView method. If you want to create your own "list items" textview layout, you can pass it to the ArrayAdapter constructor. – Ander Webbs Jan 03 '11 at 08:14
7

my problem was this: I override getView in arrayAdapter. then when I inflated view, I forgot to set attach to root to false and that caused this exception.

this is how must be done:

convertView = LayoutInflater.from(getContext()).inflate(R.layout.market_search_product_view, parent,false);

after that problem solved!

https://stacklearn.ir

Hossein Karami
  • 776
  • 10
  • 11
3

Delete TextView From ListView in your XML file

Harit Dahiya
  • 171
  • 1
  • 4
0

If you added a button/text view inside your List View, delete it. That's causing the problem.

ni6hant
  • 23
  • 6