I want my RelativeLayout to show that it is checked. For this I thought a custom RelativeLayout would be easiest, that would implement checkable. Thus when I clicked an item, this would be called and modify my layout. I thought the easiest would be to highlight it by setBackgroundColor to the holo selected blue.
Firstly is there anything wrong with the above? (i.e. does that sound like a good idea?)
Secondly, it works in that an item will be checked and if I call isChecked it returns the correct answer. However it doesn't update the RelativeLayout to indicate that it is selected. I assume there is something wrong in my code.
public class CustomRL extends RelativeLayout implements Checkable {
private boolean isChecked;
List<RelativeLayout> rl = new ArrayList<RelativeLayout>();
Context context;
public CustomRL(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
#Override
public boolean isChecked() {
return isChecked;
}
#Override
public void setChecked(boolean isChecked) {
this.isChecked = isChecked;
for (RelativeLayout r : rl)
r.setBackgroundColor(isChecked ? Color.parseColor("#33b5e5")
: 0x00000000);
}
#Override
public void toggle() {
this.isChecked = !this.isChecked;
for (RelativeLayout r : rl)
r.setBackgroundColor(this.isChecked ? Color.parseColor("#33b5e5")
: 0x00000000);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
final int childCount = this.getChildCount();
for (int i = 0; i < childCount; ++i) {
getRelativeLayout(this.getChildAt(i));
}
}
private void getRelativeLayout(View v) {
if (v instanceof RelativeLayout) {
rl.add((RelativeLayout) v);
}
}
}
This is important to use this layout properly. First of all when You use it in ListView, you should set
listView.setItemsCanFocus(false)
and set choice mode either to
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE)
or
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE)
Related
i have a big problem with creating and removing views in many times
For example i searched in sqlite database and fetching 200 records, and i want create 200view in my activity but it happen in 3 or 4 seconds and it is bad for user experiences and performances.how to increase time for creating views every time?
These are my codes for creating views
public class CreateView {
boolean header_flag=false;
boolean first_widget=false;
LinearLayout header_layout;
List<Words_taha> words_tahaList=new ArrayList<>();
public void createHeader(Context context, LinearLayout main_layout, Words_taha words){
if(header_flag==false){
header_layout =new LinearLayout(context);
header_layout=new LinearLayout(context);
header_layout.setOrientation(LinearLayout.HORIZONTAL);
header_layout.setBackgroundResource(R.mipmap.sure_template);
header_layout.setId(words.getW_id());
header_layout.setGravity(Gravity.CENTER);
main_layout.addView(header_layout);
header_flag=true;
words_tahaList.add(words);
}
else {
words_tahaList.add(words);
Collections.reverse(words_tahaList);
for(int i=0;words_tahaList.size()>i;i++){
TextView textView=new TextView(context);
textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
textView.setText(words_tahaList.get(i).getW_text()+" ");
textView.setTag(words_tahaList.get(i).getW_id());
Typeface typeface=Typeface.createFromAsset(context.getAssets(),"fonts/arabicNeirizi.ttf");
textView.setTypeface(typeface);
textView.setTextColor(Color.parseColor("#000000"));
header_layout.addView(textView);
}
words_tahaList.clear();
header_flag=false;
}
}
public void createLabelForMainWordsInOneLine(Activity context, LinearLayout main_layout, List<Words_taha> words_tahaList, int count ){
LinearLayout linearLayout=new LinearLayout(context);
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
linearLayout.setGravity(Gravity.CENTER);
if(words_tahaList.size()>0) {
main_layout.addView(linearLayout);
Collections.reverse(words_tahaList);
for (Words_taha w : words_tahaList) {
DroidTextView textView = new DroidTextView(context);
textView.setText(w.getW_text());
textView.setTag(w.getW_id());
textView.setTextColor(Color.parseColor("#000000"));
if (w.getW_type() == 3) {
textView.setLayoutParams(new LinearLayout.LayoutParams(130, 130));
textView.setGravity(Gravity.CENTER);
textView.setBackgroundResource(R.mipmap.sore);
} else {
textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
}
linearLayout.addView(textView);
}
words_tahaList.clear();
}
}
Please help me how to optimization my code
Thanks.
Well there are a views in android that made for this specific purpose, like ListView or RecyclerView and more, those views get Adapter object, the Adapter restore the data (in your case your 200 rows) and ListView For Example create only the Items That apear in the screen, And when the user scroll up ro down, ListView create the new Views that apear and delete the olds, this give to the user a fantastic use in huge lists of items, I recommend to lern use with ListView and Adapter if you must use your own custom View you can extends from ListView and implemnt in your own way.
you can read more here
if your want specific gui that diffrent from ListView or you need help let me know
Update :
if you want to create your own impl what do you think about this direction ?
public abstract class CustomBookView extends LinearLayout implements CustomBookListener {
private int pageIndex = 0;
private List<WordsTasa> wordsList;
public CustomBookView(Context context, int pageIndex, List<WordsTasa> wordsList) {
super(context);
this.pageIndex = pageIndex;
this.wordsList = wordsList;
}
public abstract View createPage(WordsTasa wordsTasa);
#Override
public void goNextPage() {
if(wordsList.size()>=pageIndex+1)
return;
this.removeAllViews();
//add your animation
this.addView(createPage(wordsList.get(++pageIndex)));
}
#Override
public void goPreviousPage() {
if(0<pageIndex-1)
return;
this.removeAllViews();
//add your animation
this.addView(createPage(wordsList.get(--pageIndex)));
}
public int getPageIndex() {
return pageIndex;
}
public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
}
public List<WordsTasa> getWordsList() {
return wordsList;
}
public void setWordsList(List<WordsTasa> wordsList) {
this.wordsList = wordsList;
}
public CustomBookView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomBookView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public static class WordsTasa {
private String words;
public WordsTasa(String words) {
this.words = words;
}
public String getWords() {
return words;
}
public void setWords(String words) {
this.words = words;
}
}
}
public interface CustomBookListener {
void goNextPage();
void goPreviousPage();
}
on your next page button or previous
button.setOnClickListener(new View.OnClickListener {
public void onClick(View v) {
customBookListener.goNextPage();
}
})
I am trying to create a list view as follows:-
Now, I have made the list view work successfully using the WearableListView adapter. However, I am using static images in the ImageView. What I want is to create this gray and blue kind of animation when a particular list element is focused while scrolling. How do I do that? I tried using a selector xml file for the ImageView but it seems this ListView doesnt make use of this selector(android:state-focused, selected, pressed-nothing works). Any idea how do I get what I want? Hope I made my question clear. Thanks.
Yes you are right, image selector doesn't work, why? I don't know exactly and we don't have any documentation on it to read.
But fortunately I have implemented this scenario successfully and it's almost similar to the default settings screen.
For workaround, I have set circle color inside below methods:
onScaleUpStart() - Set circle color for selected item
onScaleDownStart() - Set circle color for non-selected items.
I have taken CircleImageView and TextView inside my item layout. Example code could be:
private final class MyItemView extends FrameLayout implements WearableListView.Item {
final CircledImageView image;
final TextView text;
private float mScale;
private final int mFadedCircleColor;
private final int mChosenCircleColor;
public MyItemView(Context context) {
super(context);
View.inflate(context, R.layout.wearablelistview_item, this);
image = (CircledImageView) findViewById(R.id.image);
text = (TextView) findViewById(R.id.text);
mFadedCircleColor = getResources().getColor(android.R.color.darker_gray);
mChosenCircleColor = getResources().getColor(android.R.color.holo_blue_dark);
}
#Override
public float getProximityMinValue() {
return mDefaultCircleRadius;
}
#Override
public float getProximityMaxValue() {
return mSelectedCircleRadius;
}
#Override
public float getCurrentProximityValue() {
return mScale;
}
#Override
public void setScalingAnimatorValue(float value) {
mScale = value;
image.setCircleRadius(mScale);
image.setCircleRadiusPressed(mScale);
}
#Override
public void onScaleUpStart() {
image.setAlpha(1f);
text.setAlpha(1f);
image.setCircleColor(mChosenCircleColor);
}
#Override
public void onScaleDownStart() {
image.setAlpha(0.5f);
text.setAlpha(0.5f);
image.setCircleColor(mFadedCircleColor);
}
}
New update brings WearableListView.OnCenterProximityListener alternative of WearableListView.Item to implement Wearable list view with selector,
It has two methods to implement:
onCenterPosition
onNonCenterPosition
public class WearableListItemLayout extends LinearLayout implements WearableListView.OnCenterProximityListener {
private CircledImageView image;
private TextView text;
public WearableListItemLayout(Context context) {
this(context, null);
}
public WearableListItemLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WearableListItemLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
image = (CircledImageView) findViewById(R.id.image);
text = (TextView) findViewById(R.id.name);
}
#Override
public void onCenterPosition(boolean b) {
image.setAlpha(1f);
image.setBackgroundResource(R.drawable.blue_oval);
text.setAlpha(1f);
}
#Override
public void onNonCenterPosition(boolean b) {
image.setAlpha(0.5f);
image.setBackgroundResource(R.drawable.gray_oval);
text.setAlpha(0.5f);
}
}
I have a ListView with an EditText on each row working.
I need to select this text on click the edittext to write numbers without erasing or moving the cursor.
Using selectAllonfocus at first works. However, after scrolling the listview, the EditText got crazy and selection doesn't work correctly.
If I execute selectAll in the listener onFocus, then when a touch is made the contextmenu (select word, select all,etc) is shown instead of the selection.
If someone can help.
Thanks.
I can't tell what it is that you are trying to do. Perhaps it'd help if you can post some of your relevant source code...
But the fact that it gets crazy when you start scrolling makes me thing you aren't handling the convertView correctly in your adapter getView() method.
If you're having problem after scrolling it's the view recycling that's messing you up. You basically need to set up an array to hold the contents of the EditTexts so when you scroll they don't get messed up. You didn't say what was backing your list, but I've got a list with edittexts contained and here's how I handle it (from a cursor, but you could adapt it for an ArrayAdapter):
public class CursorAdapter_EditText extends SimpleCursorAdapter {
private static Cursor c;
private Context context;
public static String[] quantity;
private int layout;
public CursorAdapter_EditText(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
CursorAdapter_EditText.c = c;
this.context = context;
this.layout = layout;
initializeQuantity(); // Call method to initialize array to hold edittext info
}
public static void initializeQuantity() {
quantity = new String[c.getCount()]; // Initialize array to proper # of items
int i = 0;
while (i < c.getCount()) {
quantity[i] = ""; // set all EditTexts to empty
i++;
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = View.inflate(context, layout, null);
final int pos = position;
View row = convertView;
c.moveToPosition(position);
TextView name = (TextView) row.findViewById(R.id.ListItem1);
EditText qty = (EditText) row.findViewById(R.id.qty);
qty.setOnFocusChangeListener(new View.OnFocusChangeListener() { // Set so EditText will be saved to array when you leave it
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
LinearLayout parent = (LinearLayout) v.getParent();
EditText qtyTemp = (EditText) parent.findViewById(R.id.qty); // Get a reference to EditText (you could probaly use v here)
quantity[pos] = qtyTemp.getText().toString(); // Save contents of EditText to array
}
}
});
name.setText(c.getString(1));
unit.setText(c.getString(3));
qty.setText(quantity[position]);
return (row);
}
}
Then I have a button outside the array that processes it back into my database like this:
commit.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
int i = 0;
itemCursor.moveToFirst();
while (itemCursor.isAfterLast() == false) {
if (CursorAdapter_EditText.quantity[i].equals("")) {
CursorAdapter_EditText.quantity[i] = "0";
}
;
int tempQty = Integer
.parseInt(CursorAdapter_EditText.quantity[i]);
if (tempQty != 0) {
mDbHelper.createListItem(listId, itemCursor
.getInt(itemCursor
.getColumnIndex(GroceryDB.ITEM_ROWID)),
tempQty, 0);
}
i++;
itemCursor.moveToNext();
}
}
});
I dealt with exactly the same problem by doing the following:
Remove android:descendantFocusability="beforeDescendants" from xml with ListView, android:windowSoftInputMode="adjustPan" from AndroidManifest - they ain't help.
Subclassed EditText
public class TrickyEditText extends EditText {
private boolean mFocused = false;
private boolean mTouched = false;
public TrickyEditText(Context context) {
super(context);
}
public TrickyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TrickyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public TrickyEditText(Context context, AttributeSet attrs, int defStyleAttr, int style) {
super(context, attrs, defStyleAttr, style);
}
#Override
public boolean didTouchFocusSelect() {
if (mTouched && mFocused) {
return true;
} else {
return super.didTouchFocusSelect();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mTouched = true;
return super.onTouchEvent(event);
}
#Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
mFocused = focused;
if (!focused) {
mTouched = false;
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
}
Adapter code
public class TrickyAdapter extends ArrayAdapter {
.............
#Override
public View getView(int childPosition, View convertView, final ViewGroup parent) {
.........
TrickyEditText et = ..... //initialize edittext
et.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(final View v, boolean hasFocus) {
if (hasFocus) {
v.post(new Runnable() {
#Override
public void run() {
((EditText)v).selectAll();
}
});
}
}
});
.........
}
}
Although it's working pretty well, it's not the code I'm proud of... If somebody knows a prettier solution, please tell me!
On the getView method of your adapter, get your EditView (in this code, edittxt) and do:
edittxt.post(new Runnable() {
public void run() {
edittxt.requestFocusFromTouch();
edittxt.selectAll();
InputMethodManager lManager = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
lManager.showSoftInput(edittxt, 0);
}
});
What I can say about what I asked is that trying to select all text of the listview edittexts don't work properly.
So instead of select the text, I show a dialog where the user selects the number or whatever.
Not sure if you are still looking for a solution, but I found a way to do it. Add a focus change listener to the edittext in the getView method of your adapter and select all text when focus is received.
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
// blah blah blah
EditText edit = (EditText) view.findViewById(R.id.editTextId);
edit.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean hasFocus) {
if (hasFocus)
((EditText) view).selectAll();
}
});
return view;
}
I had set windowSoftInputMode to adjustPan for the activity, although I don't think it has any bearing on this matter.
I have tested this on Android 2.3 and 4 and it works.
i have a simple question:
suppose i have some views on a scrollView (or a horizontalScrollView) .
is there any way to add a listener that will tell me when such a view is getting inside and outside the visible area ?
the only similar question i've seen is this:
Android: how to check if a View inside of ScrollView is visible?
but i want to be informed when such an event occurs (becoming hidden/visible) .
Subclass the view classes you are using (I did this for ImageView as I was only adding those to my scroll view):
public class PeekImageView extends ImageView implements ViewTreeObserver.OnScrollChangedListener {
private static final String LOG_TAG = "PeekImageView";
private InViewportListener inViewportListener;
private boolean isInViewport = false;
public PeekImageView(Context context) {
super(context);
}
public PeekImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PeekImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public interface InViewportListener {
void onViewportEnter(PeekImageView view);
void onViewportExit(PeekImageView view);
}
public void setInViewportListener(InViewportListener listener) {
this.inViewportListener = listener;
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
ViewTreeObserver vto = getViewTreeObserver();
if (vto != null) {
vto.addOnScrollChangedListener(this);
}
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
ViewTreeObserver vto = getViewTreeObserver();
if (vto != null) {
vto.removeOnScrollChangedListener(this);
}
}
#Override
public void onScrollChanged() {
Rect bounds = new Rect();
boolean inViewport = getLocalVisibleRect(bounds);
Log.d(LOG_TAG, "is in view " + bounds + " : " + inViewport + " ; " + bounds);
if (inViewportListener != null && isInViewport != inViewport) {
if (inViewport) {
inViewportListener.onViewportEnter(this);
} else {
inViewportListener.onViewportExit(this);
}
}
isInViewport = inViewport;
}
}
Attaching an InViewportListener to an instance of this PeekImageView will get you notified whenever the view enters or leaves the visible part of the window (the viewport).
You could do something like:
1) keep a list/array of views that are contained in your ScrollView.
2) Set a listener on the scroll view for when the scroll is changed: Synchronise ScrollView scroll positions - android
3) In the listener loop through these views using the Android: how to check if a View inside of ScrollView is visible? method to see if they have gone of the screen
This is a basic method but it'll work, how fast it is depends on whats on your screen etc, but it starts you in the right direction
I've found a nice way to be notified of what i've asked about here.
it works for scrollView with vertical LinearLayout, but if you wish you can make it work for other cases too, depending on the case.
i'm not sure if i should handle onSizeChanged() method too, and if so, what to do there, but in all other cases, this code works fine.
here's the code:
MainActivity.java (for testing) :
public class MainActivity extends Activity
{
#Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final CustomScrollView scrollView=(CustomScrollView)findViewById(R.id.scrollView1);
scrollView.setOnChildViewVisibilityChangedListener(new onChildViewVisibilityChangedListener()
{
#Override
public void onChildViewVisibilityChanged(final int index,final View v,final boolean becameVisible)
{
Log.d("Applog","index:"+index+" visible:"+becameVisible);
}
});
final ViewGroup container=(ViewGroup)findViewById(R.id.linearLayout);
for(int i=0;i<20;++i)
{
final TextView tv=new TextView(this);
tv.setText("item "+i);
tv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,300));
container.addView(tv);
}
}
}
activity_main.xml
<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" >
<com.example.scrollviewvisibilitydetector.CustomScrollView
android:id="#+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout android:id="#+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</com.example.scrollviewvisibilitydetector.CustomScrollView>
</RelativeLayout>
CustomScrollView.java (the real deal...) :
public class CustomScrollView extends ScrollView
{
Set<Integer> _shownViewsIndices =new HashSet<Integer>();
onChildViewVisibilityChangedListener _onChildViewVisibilityChangedListener;
public interface onChildViewVisibilityChangedListener
{
public void onChildViewVisibilityChanged(int index,View v,boolean becameVisible);
}
public CustomScrollView(final Context context)
{
super(context);
}
public CustomScrollView(final Context context,final AttributeSet attrs)
{
super(context,attrs);
}
public CustomScrollView(final Context context,final AttributeSet attrs,final int defStyle)
{
super(context,attrs,defStyle);
}
public void setOnChildViewVisibilityChangedListener(final onChildViewVisibilityChangedListener onChildViewVisibilityChangedListener)
{
_onChildViewVisibilityChangedListener=onChildViewVisibilityChangedListener;
}
#Override
protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b)
{
super.onLayout(changed,l,t,r,b);
checkViewsVisibility(l,t);
}
private void checkViewsVisibility(final int l,final int t)
{
final ViewGroup viewGroup=(ViewGroup)getChildAt(0);
final int childCount=viewGroup.getChildCount();
if(childCount==0)
return;
final int parentBottom=t+getHeight();
// prepare to use binary search to find a view that is inside the bounds
int min=0,max=childCount-1,piv=-1;
int childTop,childBottom;
View v;
// check previously shown views
for(final Iterator<Integer> iterator=_shownViewsIndices.iterator();iterator.hasNext();)
{
final Integer cur=iterator.next();
v=viewGroup.getChildAt(cur);
childTop=v.getTop();
childBottom=v.getBottom();
if(childTop<=parentBottom&&childBottom>=t)
{
if(piv==-1)
piv=cur;
}
else
{
if(_onChildViewVisibilityChangedListener!=null)
_onChildViewVisibilityChangedListener.onChildViewVisibilityChanged(cur,v,false);
iterator.remove();
}
}
if(piv==-1)
{
// check first view
v=viewGroup.getChildAt(min);
childTop=v.getTop();
childBottom=v.getBottom();
if(childTop<=parentBottom&&childBottom>=t)
piv=min;
else
{
// check last view
v=viewGroup.getChildAt(max);
childTop=v.getTop();
childBottom=v.getBottom();
if(childTop<=parentBottom&&childBottom>=t)
piv=min;
}
if(piv==-1)
while(true)
{
piv=(min+max)/2;
v=viewGroup.getChildAt(piv);
childTop=v.getTop();
childBottom=v.getBottom();
if(childTop<=parentBottom&&childBottom>=t)
break;
if(max-min==1)
return;
if(childBottom<t)
// view above bounds
min=piv;
else max=piv;
}
}
//
for(int i=piv;i<childCount;++i)
{
v=viewGroup.getChildAt(i);
childTop=v.getTop();
childBottom=v.getBottom();
// _shownViewsIndices.
if(childTop<=parentBottom&&childBottom>=t&&!_shownViewsIndices.contains(i))
{
_shownViewsIndices.add(i);
if(_onChildViewVisibilityChangedListener!=null)
_onChildViewVisibilityChangedListener.onChildViewVisibilityChanged(i,v,true);
}
}
for(int i=piv-1;i>=0;--i)
{
v=viewGroup.getChildAt(i);
childTop=v.getTop();
childBottom=v.getBottom();
if(childTop<=parentBottom&&childBottom>=t&&!_shownViewsIndices.contains(i))
{
_shownViewsIndices.add(i);
if(_onChildViewVisibilityChangedListener!=null)
_onChildViewVisibilityChangedListener.onChildViewVisibilityChanged(i,v,true);
}
}
}
#Override
protected void onScrollChanged(final int l,final int t,final int oldl,final int oldt)
{
super.onScrollChanged(l,t,oldl,oldt);
checkViewsVisibility(l,t);
}
}
I have a custom configuration page in my app which just so happens to contain a ListView which you can select/deselect, edit, add to and remove items from. Since the amount of configuration is so large I've had to put it all in a ScrollView
My problem is of course that you cannot have scroll functionality within a view which already has it's own scroll functionality. This means I can't have a scrolling ListView inside a ScrollView.
What I've been trying to do is find the best way of limiting the damage this does. I've seen suggestions that say "You could just create a LinearLayout which grows as you add more children". That would work find by the added effort required to plug in the selectable nature, the reordering & sorting of the list as well as the editing would be a maintanance nightmare.
I've spent the day trying to find a way of measuring the height of each ListView item. Once I can find the size of each item (not just the content but any padding and space between items) on each device I know I can simply change the height of the ListView per item added.
Unfortunately I can't seem to find a way to reliably pull back the height of a listviews child.
(The old chestnut of using a GlobalLayoutListener doesn't help me pull back the padding between items)
final TextView listLabel = (TextView) toReturn.findViewById(R.id.listLabel);
final ViewTreeObserver vto = listLabel.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
listLabel.getViewTreeObserver().removeGlobalOnLayoutListener(this);
mListItemHeight = listLabel.getHeight();
}
});
Maybe you're trying to display too many details on your page? You could split the activity in a summary with buttons that lead to multiple one-screen-long activities.
In my experience, users usually prefer an uncluttered and clear view, even if that means having to click once or twice to get to the part they want.
EDIT
Expanding ListView's are you're friend - This LinearLayout expands based on it's content. It allows Dynamic ListView's inside of ScrollView.
public class LinearListView extends LinearLayout {
private BaseAdapter mAdapter;
private Observer mObserver;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
public LinearListView(Context context) {
super(context);
init();
}
public LinearListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LinearListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mObserver = new Observer();
}
public void setAdapter(BaseAdapter adapter) {
if (this.mAdapter != null)
this.mAdapter.unregisterDataSetObserver(mObserver);
this.mAdapter = adapter;
adapter.registerDataSetObserver(mObserver);
mObserver.onChanged();
}
public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
mOnItemLongClickListener = listener;
}
private int mListSelector = R.drawable.selector_list;
public void setListSelector(int resid) {
mListSelector = resid;
}
private class Observer extends DataSetObserver {
public Observer(){}
#Override
public void onChanged() {
List<View> oldViews = new ArrayList<View>(getChildCount());
for (int i = 0; i < getChildCount(); i++)
oldViews.add(getChildAt(i));
Iterator<View> iter = oldViews.iterator();
removeAllViews();
for (int i = 0; i < mAdapter.getCount(); i++) {
final int index = i;
View convertView = iter.hasNext() ? iter.next() : null;
View toAdd = mAdapter.getView(i, convertView, LinearListView.this);
toAdd.setBackgroundResource(mListSelector);
toAdd.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(null, v, index, index);
}
}
});
toAdd.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if(mOnItemLongClickListener != null) {
mOnItemLongClickListener.onItemLongClick(null, v, index, index);
}
return true;
}
});
LinearListView.this.addView(toAdd, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
super.onChanged();
}
#Override
public void onInvalidated() {
removeAllViews();
super.onInvalidated();
}
}
}