Within my current Android application, I have a screen that displays an android.support.v4.app.DialogFragment.
This DialogFragment view contains the following UI components
HEADING
== Sub Heading
== NestedScrollView
==== RecyclerView
==== RadioGroup
==== Spinner
==== EditText
==== Action Buttons
The DialogFragment is configured to be Full Screen using Style as follows:-
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.AppDialogTheme);
}
My dialog style is
<!-- Define your custom dialog theme here extending from base -->
<style name="AppDialogTheme" parent="Theme.AppCompat.Light.Dialog">
<!-- Define color properties as desired -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#000</item>
<item name="android:textColorHighlight">#color/background_url</item>
<item name="colorAccent">#color/dark_grey</item>
<item name="colorControlNormal">#color/colorPrimaryDark</item>
<!-- Define window properties as desired -->
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">#android:color/white</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
The reason I employ a NestedScrollView is so that the View will work in both Portrait and Landscape mode.
I wish to limit the height of the RecyclerView
The closest I have got is using the layout below.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/headline_literal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:padding="10dp"
android:text="Heading"
android:textSize="20sp"
android:textStyle="bold" />
<View
android:id="#+id/divider"
android:layout_width="fill_parent"
android:layout_height="2dp"
android:layout_marginTop="5dp"
android:background="#c0c0c0" />
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="5"
android:orientation="vertical">
<TextView
android:id="#+id/sub_headline_literal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="Some long texts having a long size so that it takes multiple lines in the view to replicate the real-life app use case. This is important to have 3-4 lines this textview so that we can see if the views are being populated correctly. Hope this sentence is long enough to replicate the real-life scenario of this TextView content. Thank you."
android:textSize="16sp"
android:textStyle="normal" />
<android.support.v7.widget.RecyclerView
android:id="#+id/dummy_rv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="10dp"
android:layout_marginStart="9dp"
android:layout_marginEnd="9dp"
android:layout_weight="1"
android:background="#drawable/rv_border"
android:fadingEdge="horizontal"
android:fadingEdgeLength="10dp"
android:padding="10dp"
android:requiresFadingEdge="vertical" />
<RadioGroup
android:id="#+id/myRadioGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:checkedButton="#+id/sound">
<RadioButton
android:id="#+id/sound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sound" />
<RadioButton
android:id="#+id/vibration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Vibration" />
<RadioButton
android:id="#+id/silent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Silent" />
</RadioGroup>
<EditText
android:id="#+id/notes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Notes" />
<LinearLayout
android:id="#+id/buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="#+id/cancel_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Cancel" />
<TextView
android:id="#+id/submit_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Submit" />
</LinearLayout>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
By using weightSum on the inner LinearLayout of the NestedScrollView I can limit the height of the Recyclerview. However the NestedScrollView height is far too large, with more than half its height being blank.
How can I limit the height of my RecyclerView and get NestedScrollView to wrap_content?
I've tried NestedScrollView with height wrap_content but this has no effect.
How can I achieve the desired UI? Thanks in advance!
Instead of having a NestedRecyclerView, I would like to suggest to have a header and a footer added to your RecyclerView which will nicely place the overall content as far as I have seen your layout. I want to provide you a link to my answer here where you can find how to add a footer and a header along with your RecyclerView.
Hence, I would like to suggest to create a view with headline_literal and the divider and use this as a header whereas the RadioGroup, EditText and the Button will be in the footer. Let me know if you face any problem with it.
I have tried to implement the behavior that you want by myself and let me know if the following implementation works for you. I have added this in Github as well.
Let us first declare an adapter for adding a header and a footer to the RecyclerView.
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
public class RecyclerViewWithHeaderFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int FOOTER_VIEW = 1;
private static final int HEADER_VIEW = 2;
private ArrayList<String> data; // Take any list that matches your requirement.
private Context context;
// Define a constructor
public RecyclerViewWithHeaderFooterAdapter(Context context, ArrayList<String> data) {
this.context = context;
this.data = data;
}
// Define a ViewHolder for Header view
public class HeaderViewHolder extends ViewHolder {
public HeaderViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do whatever you want on clicking the item
}
});
}
}
// Define a ViewHolder for Footer view
public class FooterViewHolder extends ViewHolder {
public FooterViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do whatever you want on clicking the item
}
});
}
}
// Now define the ViewHolder for Normal list item
public class NormalViewHolder extends ViewHolder {
public NormalViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do whatever you want on clicking the normal items
}
});
}
}
// And now in onCreateViewHolder, you have to pass the correct view
// while populating the list item.
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
if (viewType == FOOTER_VIEW) {
v = LayoutInflater.from(context).inflate(R.layout.list_item_footer, parent, false);
FooterViewHolder vh = new FooterViewHolder(v);
return vh;
} else if (viewType == HEADER_VIEW) {
v = LayoutInflater.from(context).inflate(R.layout.list_item_header, parent, false);
HeaderViewHolder vh = new HeaderViewHolder(v);
return vh;
}
// Otherwise populate normal views
v = LayoutInflater.from(context).inflate(R.layout.list_item_normal, parent, false);
NormalViewHolder vh = new NormalViewHolder(v);
return vh;
}
// Now bind the ViewHolder in onBindViewHolder
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
try {
if (holder instanceof NormalViewHolder) {
NormalViewHolder vh = (NormalViewHolder) holder;
vh.bindView(position);
} else if (holder instanceof FooterViewHolder) {
FooterViewHolder vh = (FooterViewHolder) holder;
} else if (holder instanceof HeaderViewHolder) {
HeaderViewHolder vh = (HeaderViewHolder) holder;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Now the critical part. You have return the exact item count of your list
// I've only one footer. So I returned data.size() + 1
// If you've multiple headers and footers, you've to return total count
// like, headers.size() + data.size() + footers.size()
#Override
public int getItemCount() {
if (data == null) {
return 0;
}
if (data.size() == 0) {
// Return 1 here to show nothing
return 1;
}
// Add extra view to show the header view
// Add another extra view to show the footer view
// So there are two extra views need to be populated
return data.size() + 2;
}
// Now define getItemViewType of your own.
#Override
public int getItemViewType(int position) {
if (position == 0) {
// This is where we'll add the header.
return HEADER_VIEW;
} else if (position == data.size() + 1) {
// This is where we'll add a footer.
return FOOTER_VIEW;
}
return super.getItemViewType(position);
}
// So you're done with adding a footer and its action on onClick.
// Now set the default ViewHolder for NormalViewHolder
public class ViewHolder extends RecyclerView.ViewHolder {
// Define elements of a row here
public ViewHolder(View itemView) {
super(itemView);
// Find view by ID and initialize here
}
public void bindView(int position) {
// bindView() method to implement actions
}
}
}
Now let us define the layouts one by one. Here is the list_item_normal.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">
<TextView
android:id="#+id/normal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="This is a text to be displayed in each item in the RecyclerView"
android:textSize="16sp"
android:textStyle="normal" />
</LinearLayout>
And the list_item_footer.xml should look like the following.
<?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:layout_marginTop="16dp"
android:orientation="vertical">
<RadioGroup
android:id="#+id/myRadioGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:checkedButton="#+id/sound">
<RadioButton
android:id="#+id/sound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sound" />
<RadioButton
android:id="#+id/vibration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Vibration" />
<RadioButton
android:id="#+id/silent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Silent" />
</RadioGroup>
<EditText
android:id="#+id/notes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Notes" />
<LinearLayout
android:id="#+id/buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="#+id/cancel_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Cancel" />
<TextView
android:id="#+id/submit_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Submit" />
</LinearLayout>
</LinearLayout>
Finally, the list_item_header.xml should have the following.
<?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">
<TextView
android:id="#+id/sub_headline_literal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="Some long texts having a long size so that it takes multiple lines in the view to replicate the real-life app use case. This is important to have 3-4 lines this textview so that we can see if the views are being populated correctly. Hope this sentence is long enough to replicate the real-life scenario of this TextView content. Thank you."
android:textSize="16sp"
android:textStyle="normal" />
</LinearLayout>
Now you have divided the components of your original layout into parts. Hence the main layout should look like the following.
<?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:orientation="vertical">
<TextView
android:id="#+id/headline_literal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:padding="10dp"
android:text="Heading"
android:textSize="20sp"
android:textStyle="bold" />
<View
android:id="#+id/divider"
android:layout_width="fill_parent"
android:layout_height="2dp"
android:layout_marginTop="5dp"
android:background="#c0c0c0" />
<android.support.v7.widget.RecyclerView
android:id="#+id/dummy_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp" />
</LinearLayout>
Hence, I am sharing one sample Activity to run this code which will show the overall implementation.
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private ArrayList<String> data = new ArrayList<String>();
private RecyclerViewWithHeaderFooterAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeData();
initializeRecyclerView();
}
private void initializeRecyclerView() {
mRecyclerView = findViewById(R.id.dummy_rv);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new RecyclerViewWithHeaderFooterAdapter(this, data);
mRecyclerView.setAdapter(adapter);
}
private void initializeData() {
for (int i = 0; i < 10; i++) data.add("Position :" + i);
}
}
Hope that helps!
Customize Recycler view to set maxHeight.
public class MaxHeightRecyclerView extends RecyclerView {
private int mMaxHeight;
public MaxHeightRecyclerView(Context context) {
super(context);
}
public MaxHeightRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public MaxHeightRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs);
}
private void initialize(Context context, AttributeSet attrs) {
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView);
mMaxHeight = arr.getLayoutDimension(R.styleable.MaxHeightScrollView_maxHeight, mMaxHeight);
arr.recycle();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mMaxHeight > 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxHeight, MeasureSpec.AT_MOST);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
n attrs.xml
<declare-styleable name="MaxHeightScrollView">
<attr name="maxHeight" format="dimension" />
</declare-styleable>
set RecyclerView height wrap_content in xml and maxHeight to fixwidth in dp.
The RecyclerView will consume height wrap_content till fixWidth which you set, after reaching to maxHeight, the RecyclerView will scrollable.
If you also need to limit the size of your NestedScrollView by makeing a custom NestedScrollView:
public class CustomNestedScrollView extends NestedScrollView {
private int maxHeight;
private final int defaultHeight = 200;
public CustomNestedScrollView(Context context) {
super(context);
}
public CustomNestedScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
if (!isInEditMode()) {
init(context, attrs);
}
}
public CustomNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (!isInEditMode()) {
init(context, attrs);
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
if (!isInEditMode()) {
init(context, attrs);
}
}
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.CustomNestedScrollView);
//200 is a defualt value
maxHeight = styledAttrs.getDimensionPixelSize(R.styleable.CustomNestedScrollView_maxHeight, defaultHeight);
styledAttrs.recycle();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
attr.xml
<declare-styleable name="CustomNestedScrollView">
<attr name="maxHeight" format="dimension" />
</declare-styleable>
example layout for custom NestedScrollView:
<your.package.CustomNestedScrollView
android:layout_weight="1"
app:maxHeight="90dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<!--Child view with RecyclerView here-->
</your.package.CustomNestedScrollView>
Along with this custom NestedScrollView if you apply the customization of your RecyclerView then it will work exactly how you want. I hope this helps!
I am having trouble with a scrolling ListView inside a ScrollView. I have an Activity which has some EditTexts in the top part and then a tab host with two tabs which have one ListView each. When the EditText views are focused, the soft keyboard comes up and as I have a ScrollView, the content is scrollable. But the problem comes when there are more items in ListViews (ones in tabs), I am not able to scroll the ListView, even if there are more items.
The following is the layout XML:
<?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="fill_parent"
android:background="?backgroundImage"
android:orientation="vertical">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="50dip"
android:layout_alignParentBottom="true"
android:layout_margin="10dip"
android:id="#+id/buttons">
<Button
android:text="Save"
android:layout_margin="2dip"
android:textSize="20dip"
android:id="#+id/btnSaveorUpdate"
android:layout_height="wrap_content"
android:layout_width="145dip"></Button>
<Button
android:text="Cancel"
android:layout_margin="2dip"
android:textSize="20dip"
android:id="#+id/btnCancelorDelete"
android:layout_height="wrap_content"
android:layout_width="145dip"></Button>
</LinearLayout>
<ScrollView
android:layout_above="#id/buttons"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:layout_margin="10dip">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="10dip">
<TextView
android:text="Bill details"
android:textColor="?textColorDark"
android:layout_alignParentTop="true"
android:id="#+id/txtEnterDetails"
android:textSize="25dip"
android:textStyle="bold"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginBottom="2dip"></TextView>
<LinearLayout
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_width="0dip"
android:layout_height="0dip" />
<EditText
android:layout_width="fill_parent"
android:hint="Enter data"
android:inputType="numberDecimal"
android:id="#+id/txtSample"
android:textSize="#dimen/editText"
android:layout_height="#dimen/editTextHeight"
android:text=""></EditText>
<EditText
android:layout_width="fill_parent"
android:id="#+id/txtDescription"
android:hint="Enter description"
android:textSize="#dimen/editText"
android:layout_height="#dimen/editTextHeight"
android:inputType="text"
android:text=""></EditText>
<EditText
android:layout_width="fill_parent"
android:id="#+id/txtComment"
android:hint="Enter comment (if any)"
android:textSize="#dimen/editText"
android:layout_height="#dimen/editTextHeight"
android:inputType="text"
android:text=""></EditText>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/txtDate"
android:layout_width="wrap_content"
android:text=""
android:textSize="20dip"
android:textColor="?textColorDark"
android:layout_marginLeft="10dip"
android:layout_height="#dimen/editTextHeight"
android:layout_gravity="center_vertical" />
<Button
android:id="#+id/btnPickDate"
android:layout_width="wrap_content"
android:layout_height="#dimen/editTextHeight"
android:text="Select date"
android:layout_margin="2dip"
android:textSize="15dip"
android:layout_gravity="center_vertical" />
</LinearLayout>
<TabHost
android:id="#+id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TabWidget
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#android:id/tabs"></TabWidget>
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#android:id/tabcontent">
<ScrollView
android:layout_above="#id/buttons"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:id="#+id/tab1">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TableLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<TableRow
android:id="#+id/tableRow1"
android:layout_marginLeft="2dip"
android:layout_marginRight="5dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:src="#drawable/ic_menu_invite"
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_gravity="center_vertical"></ImageView>
<TextView
android:text="Add friend"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_centerVertical="true"
android:textColor="?textColorDark"
android:textSize="#dimen/editText"
android:layout_gravity="center_vertical" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip">
<TextView
android:id="#+id/txtData1"
android:layout_width="170dip"
android:layout_height="wrap_content"
android:text="Data"
android:textSize="14dip"
android:textStyle="bold"
android:textColor="#000000">
</TextView>
<TextView
android:id="#+id/txtData2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sample"
android:textSize="13dip"
android:textColor="#000000"></TextView>
</TableRow>
</TableLayout>
<ListView
android:cacheColorHint="#00000000"
android:id="#+id/ListView01"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
</LinearLayout>
</ScrollView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:id="#+id/tab2"
android:orientation="vertical">
<TableLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<TableRow
android:id="#+id/tableRow2"
android:layout_marginLeft="2dip"
android:layout_marginRight="5dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:src="#drawable/ic_menu_invite"
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_gravity="center_vertical"></ImageView>
<TextView
android:text="Sample"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_centerVertical="true"
android:textColor="?textColorDark"
android:textSize="#dimen/editText"
android:layout_gravity="center_vertical" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip">
<TextView
android:id="#+id/txtUser1"
android:layout_width="170dip"
android:layout_height="wrap_content"
android:text="User"
android:textSize="14dip"
android:textStyle="bold"
android:textColor="#000000">
</TextView>
<TextView
android:id="#+id/txtUserData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="UserData"
android:textSize="13dip"
android:textColor="#000000"></TextView>
</TableRow>
</TableLayout>
<ListView
android:cacheColorHint="#00000000"
android:id="#+id/ListView02"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
</TabHost>
</LinearLayout>
</ScrollView>
</RelativeLayout>
Please can anyone tell me what the problem is here? I have another post on the ListView inside ScrollView problem, but they were of no use in my case.
I found a solution that works excellently and can scroll the ListView without problems:
ListView lv = (ListView)findViewById(R.id.myListView); // your listview inside scrollview
lv.setOnTouchListener(new ListView.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
// Handle ListView touch events.
v.onTouchEvent(event);
return true;
}
});
What this does is disable the TouchEvents on the ScrollView and make the ListView intercept them. It is simple and works all the time.
You shouldn't put a ListView inside a ScrollView because the ListView class implements its own scrolling and it just doesn't receive gestures because they all are handled by the parent ScrollView. I strongly recommend you to simplify your layout somehow. For example you can add views you want to be scrolled to the ListView as headers or footers.
UPDATE:
Starting from API Level 21 (Lollipop) nested scroll containers are officially supported by Android SDK. There're a bunch of methods in View and ViewGroup classes which provide this functionality. To make nested scrolling work on the Lollipop you have to enable it for a child scroll view by adding android:nestedScrollingEnabled="true" to its XML declaration or by explicitly calling setNestedScrollingEnabled(true).
If you want to make nested scrolling work on pre-Lollipop devices, which you probably do, you have to use corresponding utility classes from the Support library. First you have to replace you ScrollView with NestedScrollView. The latter implements both NestedScrollingParent and NestedScrollingChild so it can be used as a parent or a child scroll container.
But ListView doesn't support nested scrolling, therefore you need to subclass it and implement NestedScrollingChild. Fortunately, the Support library provides NestedScrollingChildHelper class, so you just have to create an instance of this class and call its methods from the corresponding methods of your view class.
I have also one solution. I always use this method. Try this
<ScrollView
android:id="#+id/createdrill_scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="15dp" >
<net.thepaksoft.fdtrainer.NestedListView
android:id="#+id/crewList"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_weight="1"
android:background="#drawable/round_shape"
android:cacheColorHint="#00000000" >
</net.thepaksoft.fdtrainer.NestedListView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="15dp" >
<net.thepaksoft.fdtrainer.NestedListView
android:id="#+id/benchmarksList"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_weight="1"
android:background="#drawable/round_shape"
android:cacheColorHint="#00000000" >
</net.thepaksoft.fdtrainer.NestedListView>
</LinearLayout>
</ScrollView>
NestedListView.java class:
public class NestedListView extends ListView implements OnTouchListener, OnScrollListener {
private int listViewTouchAction;
private static final int MAXIMUM_LIST_ITEMS_VIEWABLE = 99;
public NestedListView(Context context, AttributeSet attrs) {
super(context, attrs);
listViewTouchAction = -1;
setOnScrollListener(this);
setOnTouchListener(this);
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, -1);
}
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int newHeight = 0;
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
ListAdapter listAdapter = getAdapter();
if (listAdapter != null && !listAdapter.isEmpty()) {
int listPosition = 0;
for (listPosition = 0; listPosition < listAdapter.getCount()
&& listPosition < MAXIMUM_LIST_ITEMS_VIEWABLE; listPosition++) {
View listItem = listAdapter.getView(listPosition, null, this);
//now it will not throw a NPE if listItem is a ViewGroup instance
if (listItem instanceof ViewGroup) {
listItem.setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
listItem.measure(widthMeasureSpec, heightMeasureSpec);
newHeight += listItem.getMeasuredHeight();
}
newHeight += getDividerHeight() * listPosition;
}
if ((heightMode == MeasureSpec.AT_MOST) && (newHeight > heightSize)) {
if (newHeight > heightSize) {
newHeight = heightSize;
}
}
} else {
newHeight = getMeasuredHeight();
}
setMeasuredDimension(getMeasuredWidth(), newHeight);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, 1);
}
}
return false;
}
}
You have to just replace your <ScrollView ></ScrollView> with this Custom ScrollView like <com.tmd.utils.VerticalScrollview > </com.tmd.utils.VerticalScrollview >
package com.tmd.utils;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ScrollView;
public class VerticalScrollview extends ScrollView{
public VerticalScrollview(Context context) {
super(context);
}
public VerticalScrollview(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticalScrollview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
Log.i("VerticalScrollview", "onInterceptTouchEvent: DOWN super false" );
super.onTouchEvent(ev);
break;
case MotionEvent.ACTION_MOVE:
return false; // redirect MotionEvents to ourself
case MotionEvent.ACTION_CANCEL:
Log.i("VerticalScrollview", "onInterceptTouchEvent: CANCEL super false" );
super.onTouchEvent(ev);
break;
case MotionEvent.ACTION_UP:
Log.i("VerticalScrollview", "onInterceptTouchEvent: UP super false" );
return false;
default: Log.i("VerticalScrollview", "onInterceptTouchEvent: " + action ); break;
}
return false;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
Log.i("VerticalScrollview", "onTouchEvent. action: " + ev.getAction() );
return true;
}
}
I had a same issue and while googling I found your question. Yes marked answer worked for me also but there was some issue. Anyways I found another solution.
which works perfectly without doing any jugglery.
I know this question is asked long ago, but who ever is stuck for now can solve this by adding this line into your ListView
android:nestedScrollingEnabled="true"
For Example -
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="true" />
Here is the exact method of using Listview with in the Scrollview. All we have to handle touch events.
lstvNextTracks.setOnTouchListener(new OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
Log.e("Lisview *************", "focused");
SCView.requestDisallowInterceptTouchEvent(true);
return false;
}
});
SCView.setOnTouchListener(new OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
int arr[] = new int[] { 1, 2 };
lstvNextTracks.getLocationOnScreen(arr);
/* Get bounds of child Listview*/
int lstvTop = arr[0];
int lstvBottom = arr[1] + lstvNextTracks.getHeight();
int lstvLeft = arr[1];
int lstvRight = arr[0] + lstvNextTracks.getWidth();
float x = event.getRawX();
float y = event.getRawY();
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
/*check if child ListView bounds are touched*/
if (x > lstvTop && x < lstvBottom && y > lstvLeft && y < lstvRight)
{
SCView.clearFocus();
/*This statement tells the ScrollView to do not handle this touch event, so the child Listview will handle this touch event and will scroll */
SCView.requestDisallowInterceptTouchEvent(true);
/*The child Listview isFocusable attribute must be set to true otherwise it will not work*/
lstvNextTracks.requestFocus();
return true;
} else
return false;
} else if (event.getAction() == MotionEvent.ACTION_MOVE)
{
if (x > lstvTop && x < lstvBottom && y > lstvLeft && y < lstvRight)
{
SCView.clearFocus();
SCView.requestDisallowInterceptTouchEvent(true);
lstvNextTracks.requestFocus();
return true;
} else
return false;
} else if (event.getAction() == MotionEvent.ACTION_UP)
{
SCView.clearFocus();
SCView.requestDisallowInterceptTouchEvent(true);
lstvNextTracks.requestFocus();
return false;
} else
{
return false;
}
}
});
Use the following method and enjoy!
private void setListViewScrollable(final ListView list) {
list.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
listViewTouchAction = event.getAction();
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
list.scrollBy(0, 1);
}
return false;
}
});
list.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
list.scrollBy(0, -1);
}
}
});
}
listViewTouchAction is a global integer value.
If you can replace the line
list.scrollBy(0, 1);
with something else please share it with us.
Enjoy!
Its a bad practice to have two different scrolling views together. ListView itself has its own scrolling functionality and height is auto adjusted according to Adapter settings for your row items (Keelping in mind, we are not setting specific height to ListView in our Layout xml). However in your case, you can replace ListView with a class and this will adjust height for your ListView keeping in mind that your ListView is inside ScrollView.
This will help you:
public class ExpandableHeightListview extends ListView
{
boolean expanded = false;
public ExpandableHeightListview(Context context)
{
super(context);
}
public ExpandableHeightListview(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ExpandableHeightListview(Context context, AttributeSet attrs,int defStyle)
{
super(context, attrs, defStyle);
}
public boolean isExpanded()
{
return expanded;
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// HACK! TAKE THAT ANDROID!
if (isExpanded())
{
// Calculate entire height by providing a very large height hint.
// But do not use the highest 2 bits of this integer; those are
// reserved for the MeasureSpec mode.
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
else
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
public void setExpanded(boolean expanded)
{
this.expanded = expanded;
}
}
Now extend your ListView with this class, just change your ListView tag from your resource xml to smothing like this,
<yourpackagename.ExpandableHeightListview
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/White"
android:scrollbars="none"
android:orientation="vertical"
android:fadingEdge="none">
</cyourpackagename.ExpandableHeightListview>
This will solve your scrolling problem of ListView, as now the parent scrolling is managed by ScrollView rather then ListView.
I've similar issue and resolved by creating custom class by extending with ListView.
ScrollableListView.java
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;
public class ScrollableListView extends ListView {
public ScrollableListView (Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollableListView (Context context) {
super(context);
}
public ScrollableListView (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);
}
}
Usage:
<com.my.package.ScrollableListView
android:id="#+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Add:
android:nestedScrollingEnabled="true"
requestDisallowInterceptTouchEvent (boolean disallowIntercept)
Called when a child does not want this parent and its ancestors to intercept touch events with onInterceptTouchEvent(MotionEvent).
This parent should pass this call onto its parents. This parent must obey this request for the duration of the touch (that is, only clear the flag after this parent has received an up or a cancel.
Try this answer,
listview.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// Disallow the touch request for parent scroll on touch of child view
scrollView.requestDisallowInterceptTouchEvent(true);
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_UP:
scrollView.requestDisallowInterceptTouchEvent(false);
break;
}
return false;
}
});
The best solution is to use NestedScrollVew with RecyclerView or if you want to go with Listview then you can add header and footer view to this.
For example:
View footerView = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.outofoffice_footer_view, null, false);
infoListView.addFooterView(footerView);
I have tried and tested nearly all the methods mentioned above, trust me, after completely running away from RecyclerView, I replaced my ListView with RecyclerView and it worked perfectly. Didnt need any 3rd Party library for ExtendedHeightListView and all, just plain and simple RecyclerView.
So Heres my Layout file before recyclerView:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="5dp">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/relativeLayoutre"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
tools:context="com.example.android.udamovappv3.activities.DetailedActivity">
<android.support.v7.widget.Toolbar
android:id="#+id/my_toolbar_detail"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="top"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:theme="#style/ThemeOverlay.AppCompat.ActionBar"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
<TextView
android:id="#+id/title_name"
android:layout_width="fill_parent"
android:layout_height="128dp"
android:layout_alignParentStart="true"
android:layout_marginTop="59dp"
android:background="#079ED9"
android:gravity="left|center"
android:padding="25dp"
android:text="Name"
android:textColor="#ffffff"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="#+id/my_toolbar_detail"
app:layout_constraintVertical_bias="0.0"
tools:layout_editor_absoluteX="0dp" />
<ImageView
android:id="#+id/iv_poster"
android:layout_width="131dp"
android:layout_height="163dp"
android:layout_alignStart="#+id/my_toolbar_detail"
android:layout_below="#+id/title_name"
android:layout_marginBottom="15dp"
android:layout_marginRight="8dp"
android:layout_marginTop="15dp"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:srcCompat="#mipmap/ic_launcher" />
<Button
android:id="#+id/bt_mark_as_fav"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/iv_poster"
android:layout_alignParentEnd="true"
android:layout_marginBottom="11dp"
android:layout_marginEnd="50dp"
android:background="#079ED9"
android:text="Mark As \n Favorite"
android:textSize="10dp" />
<TextView
android:id="#+id/tv_runTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="#+id/tv_rating"
android:layout_below="#+id/tv_releaseDate"
android:layout_marginTop="11dp"
android:text="TextView"
android:textSize="20sp" />
<TextView
android:id="#+id/tv_releaseDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="#+id/tv_runTime"
android:layout_alignTop="#+id/iv_poster"
android:text="TextView"
android:textSize="25dp" />
<TextView
android:id="#+id/tv_rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="#+id/bt_mark_as_fav"
android:layout_below="#+id/tv_runTime"
android:layout_marginTop="11dp"
android:text="TextView" />
<TextView
android:id="#+id/tv_overview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/iv_poster"
android:layout_centerHorizontal="true"
android:layout_marginBottom="5dp"
android:foregroundGravity="center"
android:text="adasdasdfadfsasdfasdfasdfasdfnb agfjuanfalsbdfjbdfklbdnfkjasbnf;kasbdnf;kbdfas;kdjabnf;lbdnfo;aidsnfl';asdfj'plasdfj'pdaskjf'asfj'p[asdfk"
android:textColor="#000000"
/>
<RelativeLayout
android:id="#+id/foodItemActvity_linearLayout_fragments"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_below="#+id/tv_overview">
<TextView
android:id="#+id/fragment_dds_review_textView_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reviews:"
android:textAppearance="?android:attr/textAppearanceMedium" />
<com.github.paolorotolo.expandableheightlistview.ExpandableHeightListView
android:id="#+id/expandable_listview"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_below="#+id/fragment_dds_review_textView_label"
android:padding="8dp">
</com.github.paolorotolo.expandableheightlistview.ExpandableHeightListView>
</RelativeLayout>
</RelativeLayout>
</ScrollView>
THIS IS AFTER REPLACING MY LISTVIEW WITH ONE OF THE MANY SOLUTIONS MENTIONED ABOVE. So the problem was that the listview was not behaving properly due to 2 scrollview bug(maybe not a bug) in android.
I replaced the
with recycler view to form form my final layout.
This is my recycler view adapter:
public class TrailerAdapter extends RecyclerView.Adapter<TrailerAdapter.TrailerAdapterViewHolder> {
private ArrayList<String> Youtube_URLS;
private Context Context;
public TrailerAdapter(Context context, ArrayList<String> Youtube_URLS){
this.Context = context;
this.Youtube_URLS = Youtube_URLS;
}
#Override
public TrailerAdapter.TrailerAdapterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.trailer_layout, parent, false);
return new TrailerAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(TrailerAdapter.TrailerAdapterViewHolder holder, int position) {
Picasso.with(Context).load(R.drawable.ic_play_arrow_black_24dp).into(holder.iv_playbutton);
holder.item_id.setText(Youtube_URLS.get(position));
}
#Override
public int getItemCount() {
if(Youtube_URLS.size()==0){
return 0;
}else{
return Youtube_URLS.size();
}
}
public class TrailerAdapterViewHolder extends RecyclerView.ViewHolder {
ImageView iv_playbutton;
TextView item_id;
public TrailerAdapterViewHolder(View itemView) {
super(itemView);
iv_playbutton = (ImageView)itemView.findViewById(R.id.play_button);
item_id = (TextView)itemView.findViewById(R.id.tv_trailer_sequence);
}
}
}
And this is my RecyclerView custom layout:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="56dp"
android:padding="6dip" >
<ImageView
android:id="#+id/play_button"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_marginRight="6dip"
android:layout_marginStart="12dp"
android:src="#drawable/ic_play_arrow_black_24dp"
android:layout_centerVertical="true"
android:layout_alignParentStart="true" />
<TextView
android:id="#+id/tv_trailer_sequence"
android:layout_width="wrap_content"
android:layout_height="26dip"
android:layout_centerVertical="true"
android:layout_toEndOf="#+id/play_button"
android:ellipsize="marquee"
android:gravity="center"
android:maxLines="1"
android:text="Description"
android:textSize="12sp" />
</RelativeLayout>
And VOILA, I got the desired effect of ListView(Now RecyclerView) within a scollview.
Heres the Final Image of the UI
On a final note, I believe that replacing the RecyclerView was a better choice for me, as it improved the overall app stability, and also helped me understand RecyclerView better. If I were to suggest a solution, Im going to say replace your ListView with a RecyclerView.
I have had this error.And my solution is following as:
1. Create a custom listview which is non scrollable
public class NonScrollListView extends ListView {
public NonScrollListView(Context context) {
super(context);
}
public NonScrollListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
2. Use above custom class for xml file
<com.Example.NonScrollListView
android:id="#+id/lv_nonscroll_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</com.Example.NonScrollListView>
Hope best for you.
Above solution given by #Shailesh Rohit works perfectly fine. Some tricks has to be done.
If you are putting helper class inside the same class( main class) then make Helper class as static and getListViewSize() not be static.
Most important, write "Helper.getListViewSize(listView);" statement after setting adapter for the first time like "listView.setAdapter(myAdapter);" as well as when ever you are using "myAdapter.notifyDataSetChanged();"
Usage is shown below.
listView = (ListView) findViewById(R.id.listView);
myAdapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1, listValues);
listView .setAdapter(myAdapter);
Helper.getListViewSizelistView(listView);
myAdapter.notifyDataSetChanged();
Helper.getListViewSizelistView(listView);
Best Code
<android.support.v4.widget.NestedScrollView
android:id="#+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/btmlyt"
android:layout_below="#+id/deshead_tv">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="#+id/des_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/btmlyt"
android:background="#android:color/white"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:scrollbars="vertical"
android:paddingTop="3dp"
android:text="description"
android:textColor="#android:color/black"
android:textSize="18sp" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Here is the best example
Add NestedScrollView instead of Scrollview
Add this line in ListView android:nestedScrollingEnabled="true"
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="true"
/>
</androidx.core.widget.NestedScrollView>
Demo_ListView_In_ScrollView
==============================
package com.app.custom_seekbar;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
public class Demo_ListView_In_ScrollView extends Activity
{
ListView listview;
ArrayList<String> data=null;
listview_adapter adapter=null;
#Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
super.setContentView(R.layout.demo_listview_in_scrollview_activity);
init();
set_data();
set_adapter();
}
public void init()
{
listview=(ListView)findViewById(R.id.listView1);
data=new ArrayList<String>();
}
public void set_data()
{
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
data.add("Meet");
data.add("prachi");
data.add("shailesh");
data.add("manoj");
data.add("sandip");
data.add("zala");
data.add("tushar");
}
public void set_adapter()
{
adapter=new listview_adapter(Demo_ListView_In_ScrollView.this,data);
listview.setAdapter(adapter);
Helper.getListViewSize(listview); // set height of listview according to Arraylist item
}
}
========================
listview_adapter
==========================
package com.app.custom_seekbar;
import java.util.ArrayList;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
public class listview_adapter extends ArrayAdapter<String>
{
private final Activity context;
ArrayList<String>data;
class ViewHolder
{
public TextView tv_name;
public ScrollView scroll;
public LinearLayout l1;
}
public listview_adapter(Activity context, ArrayList<String> all_data) {
super(context, R.layout.item_list_xml, all_data);
this.context = context;
data=all_data;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View rowView = convertView;
ViewHolder viewHolder;
if (rowView == null)
{
LayoutInflater inflater = context.getLayoutInflater();
rowView = inflater.inflate(R.layout.item_list_xml, null);
viewHolder = new ViewHolder();
viewHolder.tv_name=(TextView)rowView.findViewById(R.id.textView1);
rowView.setTag(viewHolder);
}
else
viewHolder = (ViewHolder) rowView.getTag();
viewHolder.tv_name.setText(data.get(position).toString());
return rowView;
}
}
===================================
Helper class (source: https://www.androidhub4you.com/2012/12/listview-into-scrollview-in-android.html)
====================================
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.ListView;
public class Helper {
public static void getListViewSize(ListView myListView) {
ListAdapter myListAdapter = myListView.getAdapter();
if (myListAdapter == null) {
//do nothing return null
return;
}
//set listAdapter in loop for getting final size
int totalHeight = 0;
for (int size = 0; size < myListAdapter.getCount(); size++) {
View listItem = myListAdapter.getView(size, null, myListView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
//setting listview item in adapter
ViewGroup.LayoutParams params = myListView.getLayoutParams();
params.height = totalHeight + (myListView.getDividerHeight() * (myListAdapter.getCount() - 1));
myListView.setLayoutParams(params);
// print height of adapter on log
Log.i("height of listItem:", String.valueOf(totalHeight));
}
}
========================
demo_listview_in_scrollview_activity.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="match_parent"
android:orientation="vertical" >
<ScrollView
android:id="#+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
</ScrollView>
</LinearLayout>
================
item_list_xml.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="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="TextView"
android:textSize="14sp" />
</LinearLayout>
As According to me while you are using setOnTouchListener om parent or child stop the scrolling of parent as you touch on child else stop scrolling of child as you touch on parent
Try this with ScrollView, not with ListView.
public class xScrollView extends ScrollView
{
;
;
#Override
public boolean onInterceptTouchEvent (MotionEvent ev)
{
return false;
}
}
replace ListView by RecycleView inside ScrollView. it runs smoothly without additional source code:
<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:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.hung.recycleviewtest.MainActivityFragment"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycle_view"
android:layout_width="match_parent"
android:layout_height="400dp"
android:background="#android:color/darker_gray"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recycle_view_a"
android:layout_marginTop="40dp"
android:layout_below="#id/recycle_view"
android:layout_width="match_parent"
android:layout_height="400dp"
android:background="#android:color/darker_gray"/>
</RelativeLayout>
</ScrollView>
For ListView inside ScrollView use NestedScrollView it can handle this
functionality very easily:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dip"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
I found a solution for that,
Instead of using scrollview, You can use addHeaderview and addFooterview of listview
Here is my snippet,
// create separate layout and add it dynamically
scrollview=getLayoutInflater().inflate(R.layout.yourlayout,null);
lv=(ListView)listview.findViewById(R.id.listView2);
lv.addHeaderView(scrollview);
lv.setContentView(lv);
Now, with the listview your layout also can be scrolled. Enjoy it!!!
Replace ScrollView by android.support.v4.widget.NestedScrollView inside xml. it runs
1) Use in XML:::: android.support.v4.widget.NestedScrollView
instead of:::: ScrollView
2) And use for list view in this way using NonScrollListView in cs file:
public class NonScrollListView extends ListView {
public NonScrollListView(Context context) {
super(context);
}
public NonScrollListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
3) Finally use this code to identify scrollview in cs file:
NonScrollListView listView = (NonScrollListView) view.findViewById(R.id.listview);
You cannot add a ListView in a scroll View, as list view also scolls and there would be a synchonization problem between listview scroll and scroll view scoll.
You can make a CustomList View and add this method into it.
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* Prevent parent controls from stealing our events once we've gotten a touch down
*/
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
ViewParent p = getParent();
if (p != null) {
p.requestDisallowInterceptTouchEvent(true);
}
}
return false;
}
Dont give scroll view to ListView, coz it has that setting default
<ListView
android:cacheColorHint="#00000000"
android:id="#+id/ListView01"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />