2

This a ListView screenshot of my problem:

enter image description here

This is the layout XML:

<LinearLayout 
    android:id="@+id/viewer_top"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/background_dark"
    android:orientation="vertical" >

    <EditText
        android:id="@+id/viewer_filter"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:drawableRight="@android:drawable/ic_menu_search"
        android:hint="@string/hint_filter"
        android:background="@android:color/white"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="4dp"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="3dp"
        android:inputType="text"
        android:paddingLeft="4dp"
        android:selectAllOnFocus="true" >
    </EditText>

    <EditText
        android:id="@+id/viewer_search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:drawableRight="@android:drawable/ic_menu_search"
        android:hint="@string/hint_search"
        android:background="@android:color/white"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="4dp"
        android:layout_marginTop="3dp"
        android:layout_marginBottom="5dp"
        android:inputType="text"
        android:paddingLeft="4dp"
        android:selectAllOnFocus="true" >
    </EditText>
</LinearLayout>

<HorizontalScrollView
    android:id="@+id/viewer_hscroll"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/viewer_top" >
    <ListView
        android:id="@+id/viewer_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
    </ListView>
</HorizontalScrollView>

There are 3 problems in this scenario:

  1. The Horizontal scrollview does not cover the full screen width (I drew a thick red line to mark the end)
  2. The Horizontal scrollview does not scroll horizontally
  3. The ListView rows are not of uniform width (this can be seen by the background color ending) (see the getView code below for details)

    private static final int listRowLayout = android.R.layout.activity_list_item;
    private Map<String, Integer> mColors = new HashMap<String, Integer>();
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // No logs here to keep ListView performance good
        ViewHolder holder;
        int color;
    
        if( convertView == null ) {
            convertView = mInflater.inflate(listRowLayout, parent, false);
            holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(android.R.id.text1);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        String data = mData.get(position);
    
        // A compiled regex is faster than String.Contains()
        Matcher m = ViewHolder.regex.matcher(data);
        if( m.find() ) {
            color = mColors.get(m.group(1));
        } else {
            color = mColors.get("V");
        }
    
        holder.text.setText(data);
        holder.text.setBackgroundColor(color);
        return convertView;
    }
    
    private static class ViewHolder {
        TextView text;
        static Pattern regex = Pattern.compile(" ([VIDWEF])/");
    }
    

    }

ilomambo
  • 8,290
  • 12
  • 57
  • 106
  • 1
    You listview width needs to be match_parent not wrap_content – Matthew Feb 20 '13 at 16:17
  • @Matthew Tested, and it does solve problems #1 and #2, problem #3 still exists. Please post your comment as answer, and if you have an insight for problem #3 solution you are welcome to explain. – ilomambo Feb 20 '13 at 16:27
  • Strange thing though, the lint in Eclipse shows a warning that for ListView, `android:layout_width` should be **wrap_content** – ilomambo Feb 20 '13 at 16:34
  • As for the rows, I would use an adapter which will allow you to customize the style of the row along with it's height. Here is a pretty basic example: http://www.mkyong.com/android/android-listview-example/ – Matthew Feb 20 '13 at 16:34
  • @Matthew No luck this time. I tried the customlayout, but still the background color extends only as far as the text in the TextView. – ilomambo Feb 20 '13 at 16:43
  • Could you provide some clarity as to why you are using a horizontal scrollview and not a vertical one? – Matthew Feb 20 '13 at 16:47
  • @Matthew The ListView scrolls vertically, but since each row in the List may be longer than the screen width I want t allow horizontal scrolling so the user can see it all. ::: Again, please post an answer, comments cannot be marked as answers. – ilomambo Feb 20 '13 at 16:51

4 Answers4

1

I encountered the exact same issue in trying to display a log file. I have a dedicated activity to display the log file:

protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_view_log);

    // Read in lines from the log file
    File clientLogFile = new File(LOG_FILE);
    ArrayList<String> lines = new ArrayList<String>();
    try
    {
        Scanner scanner = new Scanner((Readable) new BufferedReader(new FileReader(clientLogFile)));

        try
        {
            while(scanner.hasNextLine())
            {
                lines.add(scanner.nextLine());
            }
        }
        finally
        {
            scanner.close();
        }
    }
    catch (FileNotFoundException e)
    {
        lines.add("No log file");
    }

    // Create a simple adaptor that wraps the lines for the ListView
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item,lines);

    // Create a ListView dynamically to overide onMeasure()
    final ListView listView = new ListView(this)
    {
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            // Override onMeasure so we can set the width of the view to the widest line in the log file
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

            // Find maximum width of item in list and set scroll width equal to that
            int maxWidth = 0;
            for(int i=0; i<getAdapter().getCount(); i++)
            {
                View listItem = getAdapter().getView(i, null, this);
                listItem.measure(0, 0);
                int width = listItem.getMeasuredWidth();
                if(width > maxWidth)
                {
                    maxWidth = width;
                }
            }

            // Set width of measured dimension
            setMeasuredDimension(maxWidth, getMeasuredHeight());
        }
    };

    // Add to scroll view
    HorizontalScrollView horizontalScrollView = (HorizontalScrollView)findViewById(R.id.logScrollView);
    horizontalScrollView.addView(listView);

    // Set adaptor
    listView.setAdapter(adapter);

    // Enable fast scroll
    listView.setFastScrollEnabled(true);

    // Scroll to end
    listView.post(new Runnable(){
        public void run() {
            listView.setSelection(listView.getCount() - 1);
        }});
}

The onCreate method reads the log file and then dynamically adds a ListView to a HorizontalScrollView with onMeasure() overridden. The onMeasure() code determines the maximum width of the views in the adaptor and sets the ListView width to be that.

My activity_view_log.xml layout file is therefore very simple:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="5dp"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    android:paddingTop="5dp"
    >

    <HorizontalScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/logScrollView">
    </HorizontalScrollView>
</RelativeLayout>

In order to have finer grained control of the lines in the ListView I give my adapter my own layout file in list_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@android:id/text1"
          android:layout_width="wrap_content"
          android:layout_height="match_parent"
          android:textAppearance="?android:attr/textAppearanceListItemSmall"
          android:inputType="text|none"
    />

At the end of by onCreate() I enable fast scroll and also scroll to the end of the lines in the log file.

Paul Netherwood
  • 396
  • 2
  • 8
0

I would probably reverse what you are doing. Create a ListView and make each item in the listview horizontally scrollable. This way items only scroll when they need to, and it does not scroll the entire screen. And you get complete control over the dimensions of each list item. To do this use a custom listview adapter as mentioned in the comments. There is also a possible duplicate of your question here: Android horizontal scroll list

Community
  • 1
  • 1
Matthew
  • 3,411
  • 2
  • 28
  • 28
  • No way, I want the whole screen to scroll horizontally and vertically as one table, not each row independently. – ilomambo Feb 20 '13 at 17:20
0

In order to solve the 3 problems I had to make all the components (the horizontal scroll view, the list view and it's items) have a "fill_parent" width (I think it's the same as "match_parent"). In addition I had the listview's onMeasure(...) overridden to calculate the max width of it's items and set it via setMeasuredDimension(...). This will measure the view by it's widest item, not by it's first, as it is implemented now.

stan0
  • 11,549
  • 6
  • 42
  • 59
  • I already have all with match_parent (fill_parent is deprecated since API 8, see http://developer.android.com/reference/android/view/ViewGroup.LayoutParams.html ). Can you post the code for the onMeasure() manipulation you made? – ilomambo Feb 20 '13 at 17:25
0

This is the solution I found.
The root of all evil :-) is that ListView is not designed to efficiently deal with rows of different length. To determine the ListView width, instead of looking at all the rows, only 3 rows are taken as average.
So, if the 3 rows are by chance short rows, the width will be clipped for the longer rows, it explains the problems I experienced.

To bypass this I calculated the maximum row length for all data, and I padded shorter rows with spaces, it solved all 3 problems I described in the question.

The code for padding (executed inside getView() )

holder.text.setText(String.format("%1$-" + mLen + "s", data));
ilomambo
  • 8,290
  • 12
  • 57
  • 106