I have a linear layout include a list view and a edit text which is below the listview. Then, I put all of them in a scrollview. I want add item for the listview dynamically. If there is only listview, everything is fine. However, when I add the edit text below the listview, the height of the listview doesn't change although I use adapter.notifyDataSetChanged() function. I want when I put item in the listview, the edit text is also pushed down. What can I solve my issue? Thanks
My layout is quite simple like this
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ListView>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="example">
</EditText>
</ScrollView>
</LinearLayout>
UPDATE MY DETAIL DESIGN
Try this
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
</ListView>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="example">
</EditText>
</LinearLayout>
Mistakes you did in your code
1) Including Listview inside Scrollview is very bad practice.
2) When you deal with LinearLayout wight attribute plays the keyrole. And you have not used that attribute
If this solution doesn't work provide me dummy screen shot of the layout which you want to design. I will help you.
Have you tried using a relative layout? Then you can add the following to your edit text in xml: android:layout_below="#+id/your_listview_id"
EDIT
The adapter
public class ListAdapter extends BaseAdapter{
List<String> itemList = new ArrayList<String>();
Context context;
void addItem(String item){
itemList.add(item);
}
public ListAdapter(Context context){
this.context = context;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return itemList.size();
}
#Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return 0;
}
#Override
public View getView(int position, View arg1, ViewGroup arg2) {
// TODO Auto-generated method stub
View list;
LayoutInflater inflater = getLayoutInflater();
list = inflater.inflate(R.layout.list_item, null);
TextView text = (TextView) list.findViewById(R.id.text_view);
text.setText(itemList.get(position));
if (itemList.get(position).toString().trim().equals("edit")){
// Add a Edit text dynamically
text.setText("Edit text here");
}
return list;
}
}
The onCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_uga);
ListView list = (ListView) findViewById(R.id.list_view);
List<String> list_content = new ArrayList<String>();
listAdapter = new ListAdapter(getApplicationContext());
list_content.add("1");
list_content.add("2");
list_content.add("3");
list_content.add("4");
list_content.add("5");
list_content.add("6");
list_content.add("7");
list_content.add("8");
list_content.add("9");
list_content.add("10");
list_content.add("edit");
list.setAdapter(listAdapter);
for (String text : list_content){
listAdapter.addItem(text);
}
}
1) Don't put your listview into the scrollview, listview is scrollable.
2) If you want to fix your edittext below listview and be onscreen all the time, you can get it when compound linearlayout and relativelayout, smth like that:
<LinearLayout>
<RelativeLayout weight='1' height='wrap_content'>
<YouListView />
</RelativeLayout>
<EditText />
</LineadLayout>
In this case you RelativeLayout will fill all possible area excluding edittext's area.
3) If you want this edittext to be a part of listview use footer in listview instead.
4) You can use different layouts for each row in list but in this case you have to manage this views more accurate to avoid performance issues.
After some efforts, I solved my problem by adding the header and footer to the listview without measured the height of the list.
It made me confused when getting the item but somehow, I got it.
ListView listView = (ListView) getActivity().findViewById(
android.R.id.list);
headerView = View.inflate(getActivity(), R.layout.fragment_dish_step_header, null);
listView.addHeaderView(headerView);
TextView mDishName = (TextView) headerView.findViewById(
R.id.tv_dish_name_in_step);
mDishName.setText(mGlobal.getDishName());
footerView = View.inflate(getActivity(), R.layout.fragment_dish_step_footer, null);
listView.addFooterView(footerView);
I finally solved my problem by calculate the size of the listview in the onDataSetChanged function
ListAdapter mAdapter = listView.getAdapter();
int totalHeight = 0;
for (int i = 0; i < mAdapter.getCount(); i++) {
View mView = mAdapter.getView(i, null, listView);
mView.measure(
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
totalHeight += mView.getMeasuredHeight();
Log.w("HEIGHT" + i, String.valueOf(totalHeight));
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight
+ (listView.getDividerHeight() * (mAdapter.getCount() - 1));
listView.setLayoutParams(params);
Related
I have been trying to make a layout for my app, where I set the android:layout_height="wrap_content".
This is my code ::
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.hp.money.DashBoard">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:id="#+id/cv_amount_display"
android:layout_width="match_parent"
android:layout_height="200dp">
.....
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
card_view:cardCornerRadius="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/last_10_transaction" />
<ListView
android:id="#+id/last_transactions_lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</ScrollView>
I am using a ListView inside a ScrollView, which is a bad idea, I know! It leads to some scrolling issues, I believe. But I have to dynamically update data on my screen during runtime, and I think only the listview can handle it. If there's any other View to do it, please suggest.
Now, the problem is that even if the Data source of the ListView has 10 items, the height of the ListView always remains equal to the size of one listView element, but the height of the listview is set to wrap_content, so probably it is supposed to resize accordingly. But it doesn't! How to fix this?
This is how it looks, even though the Listview has 10 items!
The ListView is the one which has a heading as LAST 10 TRANSACTIONS
Why I used a ScrollView??
I put a scrollview, because, the number of enteries in the listview can be many, so I want that when the user scrolls to see the entries not currently visible int the listview, the whole page gets scrolled, and not only the listview.
What do you mean by " dynamically insert data into the Listview during runtime". If you are modifying list data dynamically then ListView can handle it internally. You no need to do anything in that case. So you can remove the ScrollView so it works fine.
You can not add a ListView inside ScrollView .
For considering your needs you can set ListView height dynamically to fulfill your needs ...
After setting up your list adapter simply call this method...
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
As others pointed, you can't put a ListView inside a ScrollView (anyways it doesn't make sense, ListView has its own scroll)
But I have to dynamically insert data into the Listview during runtime, if there's any other way to do it, please suggest.
I can't see why you think you can't, and want to know why you think a ScrollView will do it.
If you have created your own adapter to fill that ListView you should have something like this:
class listViewAdapter extends BaseAdapter
{
public View getView(int position, View convertView, ViewGroup parent)
{
}
public int getCount()
{
}
public Object getItem(int position) {
}
public long getItemId(int position){
}
}
Then you should create a data field and a setter
private Arraylist<DataType> data;
public void setData(Arraylist<DataType> data)
{
data = data;
}
Then you can use it like this:
ListView lv = findViewById...
ListViewAdapter adapter = new ListViewAdapter();
adapter.setData(data);
lv.setAdapter(new ListViewAdapter());
When you have added or deleted data you can reload it by calling
adapter.notifySetDataChanged();
I have a list view with 20 rows and I want to setup a horizontal scrollview for every row item in list view, as each row contains more than one item.
Here is my code :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<HorizontalScrollView
android:id="#+id/hor_scroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none" >
<LinearLayout
android:id="#+id/mainLinear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
Inner Row Layout which is to be replicated in a row any number of times
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6.0dip"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:text="Example Value"
android:textAppearance="?android:textAppearanceMedium" />
BaseAdapter
public class HorizontalListViewAdapter extends BaseAdapter {
private Context context;
private ArrayList<ArrayList<DwivediJi>> dataSet;
public HorizontalListViewAdapter(Context context) {
this.context = context;
}
#Override
public int getCount() {
return 20;
}
#Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return arg0;
}
#Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.horizontal_list_item, null,false);
LinearLayout mainLinnerLayout=(LinearLayout)convertView.findViewById(R.id.mainLinear);
for (int i = 0; i <5; i++) {
View additionView = inflater.inflate(R.layout.inner_layout_file, null,false);
LinearLayout innerLinnerLayout=(LinearLayout)additionView.findViewById(R.id.inner_layout);
mainLinnerLayout.addView(innerLinnerLayout);
}
return convertView;
}
class ViewHolder {
TextView tv_titleExample;
HorizontalScrollView hzView;
LinearLayout linear_layout,main_linear_layout;
}
}
My Problem
Look at the screenshot attached. My problem is that more than one view is showing at one time in each row.
I want that only one view should show to the user at one time and for the rest all the user has to do is swipe left to right or right to left.
Note: Not an ideal solution, but should provide what you want..
Another Note: This may make your listview slightly janky, depending on the layout-complexity
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.horizontal_list_item, null,false);
LinearLayout mainLinnerLayout=(LinearLayout)convertView.findViewById(R.id.mainLinear);
for (int i = 0; i <5; i++) {
View additionView = inflater.inflate(R.layout.inner_layout_file, null,false);
LinearLayout innerLinnerLayout=(LinearLayout)additionView.findViewById(R.id.inner_layout);
// If the width varies for each innerLinnerLayout, then remove the if block & always calculate padding value
// padding is an integer initialized to -1 in the constructor
if (padding == -1) {
int width = context.getResources().getDisplayMetrics().widthPixels;
innerLinnerLayout.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
padding = width - additionView.getMeasuredWidth();
}
// I've set padding to right only, but you could center it by giving left and right padding of value=(padding/2)
innerLinnerLayout.setPadding(0, 0, padding, 0);
mainLinnerLayout.addView(innerLinnerLayout);
}
return convertView;
}
The problem is with your adapter as:
getCount() from your adapter must return the total number from list. Returning 20 is not valid in your context - and you seem to have 20 items in your list. You should return dataSet.size();
getItem() should return the item from the model data structure, in this case:
Below
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return dataSet.get(position);
}
Also, your getView method must return the view that displays the model data at given at position parameter. Returning a ViewGroup with non-meaningfull dummy data is what you currently have. You should get the ArrayList<DwivediJi> from parameter position (through dataSet.get(position)) and construct/inflate a View that displays properly this data structure item.
Change inner layout width to match_parent. This should help you
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6.0dip"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:text="Example Value"
android:textAppearance="?android:textAppearanceMedium" />
</LinearLayout>
Seems you are adding your inner layout in this LinearLayout
<LinearLayout
android:id="#+id/mainLinear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</LinearLayout>
Did you try changing its width to match_parent too??
Use scrollview in xml layout and then dynamically create HorizontalScrollView inside for loop:
example :
for(int i.......) //number of HorizontalScrollView needed
{
HorizontalScrollView mainlinear = new HorizontalScrollView(
getApplicationContext());
for(int i.......) //number of items wants to add in HorizontalScrollView
{
// adding any widgets as per your requirements
}
mainlinear.addView(Widgetname);
scroll.addView(mainlinear);
}
I used custom listview . Content comes from dynamic I want to same listview height same as content.
I used wrap_content but this does not help If I remove scrollview then its work
Code. "The vertically scrolling ScrollView should not contain another vertically scrolling widget (ListView)"
<ScrollView 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:background="#drawable/background_img"
android:scrollbars="none" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/lstsemtrack"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="#f2e4e4"
android:dividerHeight="1dip"
>
</ListView>
Item list
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dip"
android:scrollbars="none" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="4" >
<TextView
android:id="#+id/txtBiology"
style="#style/sem_rowtext"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="5dip"
android:layout_weight="1"
android:text="Biology" />
<TextView
android:id="#+id/txtClass"
style="#style/sem_rowtext"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Biology - 101" />
<TextView
android:id="#+id/txtGrade"
style="#style/sem_rowtext"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Grade" />
<TextView
android:id="#+id/txtGrade"
style="#style/sem_rowtext"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Remove" />
</LinearLayout>
</LinearLayout>
output like below I want same as content no scrollview
Use this code
public class MyListView extends ListView {
public MyListView (Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView (Context context) {
super(context);
}
public MyListView (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
You shouldn't put a ListView inside a ScrollView because ListView itself is a view group that displays a list of scrollable items. If you use it inside scrollview it will not receive scroll events because they all are handled by the parent ScrollView. Using a ListView to make it not scroll is extremely expensive and goes against the whole purpose of ListView. You should NOT do this. Just use a LinearLayout instead.
However, if you really want to do this you can have a look at this: How can I put a ListView into a ScrollView without it collapsing?
In your parent layout set the height to wrap_content. This should work.
Other suggestions will not work, because there is no way to determine the height of the content of the ListView until after it's drawn into the screen (unless of course you have a fixed height, then use that as your ListView's height also).
What you can do is set the ListView's height AFTER drawing the elements inside it. You can do this in onWindowFocusChanged in your activity. As an example:
public void onWindowFocusChanged(boolean hasFocus) {
// get content height
int contentHeight = listView.getChildAt(0).getHeight();
// set listview height
LayoutParams lp = listView.getLayoutParams();
lp.height = contentHeight;
listView.setLayoutParams(lp);
}
Use Own Custom ListView
Create a class CustomListView.java and extend ListView like this..
public class CustomListView extends ListView {
private android.view.ViewGroup.LayoutParams params;
private int prevCount = 0;
public CustomListView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
#Override
protected void onDraw(Canvas canvas)
{
if (getCount() != prevCount)
{
int height = getChildAt(0).getHeight() + 1 ;
prevCount = getCount();
params = getLayoutParams();
params.height = getCount() * height;
setLayoutParams(params);
}
super.onDraw(canvas);
}
}
Then use it in xml
<yourPackageName.CustomListView
android:id="#+id/lstsemtrack"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="#f2e4e4"
android:dividerHeight="1dip">
</yourPackageName.ListView>
As noted by Permita you should not have a ListView in a Scrollview. You should use for example a LinearLayout instead.
In some cases you have already made custom adapters you want to reuse. In that case these code snippets may be useful.
Old view:
...
<ListView android:id="#+id/myListView" ... />
...
New view:
...
<LinearLayout
android:orientation="vertical"
android:id="#+id/myLinearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
...
Old code: (in dialog/fragment/activity)
YourAdapter listAdapter;
...
ListView listView = (ListView)rootView.findViewById(R.id.myListView);
...
if (listView != null) {
if (listAdapter == null) {
listAdapter = new YourAdapter(...);
listView.setAdapter(listAdapter);
} else {
listAdapter.notifyDataSetChanged();
}
}
New code: (doing some "listview"-stuff ourselves, to reuse views if possible)
YourAdapter listAdapter;
...
LinearLayout linearLayout = (LinearLayout) rootView.findViewById(R.id.myLinearLayout);
...
if (linearLayout != null) {
if (listAdapter == null) listAdapter = new YourAdapter(...);
for (int pos = 0; pos < listAdapter.getCount(); pos++) {
View existingView = linearLayout.getChildAt(pos);
if (existingView != null) listAdapter.getView(pos, existingView, linearLayout);
else linearLayout.addView(listAdapter.getView(pos, null, linearLayout));
}
while (linearLayout.getChildCount() > listAdapter.getCount()) linearLayout.removeViewAt(listAdapter.getCount());
}
UPDATE:
Or just add this to your adapter and call it instead of setAdapter on the list view and and notifyDataUpdated on the adapter:
public void updateOnLinearView(ViewGroup parentView) {
// Note reusing child views if there are any
for (int pos = 0; pos < getCount(); pos++) {
View existingView = parentView.getChildAt(pos);
if (existingView != null) getView(pos, existingView, parentView);
else parentView.addView(getView(pos, null, parentView));
}
while (parentView.getChildCount() > getCount())
parentView.removeViewAt(getCount());
}
couple of things to note here..
Do not use ListView inside the ScrollView
You will need to dynamically calculate the listView items to get the height for it.
Here is a function to do that..
public void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
if (listItem instanceof ViewGroup)
listItem.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
You will need to call the same in
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
setListViewHeightBasedOnChildren(listview)
}
try wrapping your list view with a LinearLayout and set the LinearLayout's height to wrap_content.
The problem is the "ListView" inside "ScrollView"
ListView have already an automatic scroll, so you can delete it
Use android:layout_height="match_parent", it will work I think.
This a ListView screenshot of my problem:
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:
The Horizontal scrollview does not cover the full screen width (I drew a thick red line to mark the end)
The Horizontal scrollview does not scroll horizontally
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])/");
}
}
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.
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
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.
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));
I trying to write code to highlight the selected value of the list with "Next" button at the bottom of the layout. But for some reason, after every list item, "next" button also shows up. Can someone please help me resolve this problem?
Here is the layout file:
<LinearLayout 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:orientation="vertical"
android:id="#+id/questionLayout"
>
<TextView android:id="#+id/txtExample"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="25sp"
android:textColor="#000000"
android:background="#FF0000"
/>
<ListView
android:id="#+id/listExample"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#CCCCCC"
android:choiceMode="singleChoice"
/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<Button
android:id = "#+id/next"
android:text="Next"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_gravity="center"
android:layout_weight="50"
/>
<Button
android:id = "#+id/submit"
android:text="Submit"
android:layout_width = "0dp"
android:layout_height = "wrap_content"
android:layout_weight="50"
android:layout_gravity="center"
/>
</LinearLayout>
</LinearLayout>
Java Code:
public class updateList extends Activity {
private SelectedAdapter selectedAdapter;
private ArrayList<String> list;
int correct_answer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list = new ArrayList<String>();
list.add("Choice One");
list.add("Choice Two");
list.add("Choice Three");
selectedAdapter = new SelectedAdapter(this,0,list);
selectedAdapter.setNotifyOnChange(true);
ListView listview = (ListView) findViewById(R.id.listExample);
listview.setAdapter(selectedAdapter);
listview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View view,
int position, long id) {
// user clicked a list item, make it "selected"
selectedAdapter.setSelectedPosition(position);
}
});
}
}
Thanks in advance
SSP
Selected Adaptor class:
public class SelectedAdapter extends ArrayAdapter{
// used to keep selected position in ListView
private int selectedPos = -1; // init value for not-selected
public SelectedAdapter(Context context, int textViewResourceId,
List objects) {
super(context, textViewResourceId, objects);
}
public void setSelectedPosition(int pos){
selectedPos = pos;
// inform the view of this change
notifyDataSetChanged();
}
public int getSelectedPosition(){
return selectedPos;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
// only inflate the view if it's null
if (v == null) {
LayoutInflater vi = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.activity_main, null);
}
// get text view
TextView label = (TextView)v.findViewById(R.id.txtExample);
// change the row color based on selected state
if(selectedPos == position){
label.setBackgroundColor(Color.CYAN);
}else{
label.setBackgroundColor(Color.WHITE);
}
label.setText(this.getItem(position).toString());
/*
// to use something other than .toString()
MyClass myobj = (MyClass)this.getItem(position);
label.setText(myobj.myReturnsString());
*/
return(v);
}
}
change your listview in xml as like this
<ListView
android:id="#+id/listExample"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"//===== set maximum heighthere
android:layout_marginBottom="50dp"// === give some space at bottom so that buttons will appear
android:background="#CCCCCC"
android:choiceMode="singleChoice"
/>
But for some reason, after every list item, "next" button also shows up.
The ListView's row layout is determined by the layout you inflate in getView() or pass to your Adapter's super class if you haven't overridden getView(). Double check this layout and remove the unwanted code.
Addition
The layout for your ListView's items only needs to be one TextView since you only want to display a phrase in each. However you are currently passing your entire main layout, this creates the Buttons, an unused ListView, and everthing else in every row...
Instead use android.R.layout.simple_list_item_1 in getView(), of course you'll need to change the id you pass to findViewById() as well:
if (v == null) {
LayoutInflater vi = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(android.R.layout.simple_list_item_1, null);
}
// get text view
TextView label = (TextView)v.findViewById(android.R.id.text1);
Please watch Android's Romain Guy discuss writing an efficient adapter to speed things up.