Here's the scenario conceptually (excluding linearlayouts)
ScrollView
Button
Checkboxes
Spinner
ListView (full-size, non-scrolling)
AdMob advert
i.e. a scrolling pane, which has a filtering UI at the top, followed by results, but the advert must always remain visible, and when scrolling down the filter UI must scroll away, leaving maximum space for results.
I'm aware there are issues with a ListView inside a ScrollView, though for me it is working well in many ways (I'm fixing the length of the ListView to stop it collapsing). So the screen scrolls nicely, the ad stays put at the bottom, and it looks good.
But the problem I'm seeing is, inexplicably, when the activity opens, the ScrollView is scrolled down a bit, so the ListView is at the top of the screen. I assume this is a default behaviour, so I set about trying to force the scroll position of the ScrollView to the top, but I've tried various methods, and see no effect:
scrollview.scrollTo(0, 1000/-1000);
scrollview.smoothScrollBy(0, 1000/-1000);
scrollview.fullScroll(View.FOCUS_UP);
Is there any way to force the ScrollView to start with the scroll position at the top?
If not, how can I have an ad that doesn't scroll off the bottom, but a filter UI that always scrolls off the top? Using ListView seems overkill as I don't need scrolling but it does provide many benefits so would be nice to avoid starting from scratch and rendering everything myself.
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.
Why are you using a listview if you're not scrolling? Why can't you just use a linearlayout or something more fit to this situation? You mention a filter, you could very easily roll your own filter especially since apparently you just have a few items in your listview.
Use something other that a ListView, you can dinamically generate linearLayouts to show the data you want. You should never use a listview inside a scrollView, it doesnt work for a simple reason, when you scroll, what should scroll, the listview or the scroll view. A couple of people from google have stated not to do this.
I've experienced the issue of a ScrollView starting off scrolled down slightly, the solution was to post a runnable which called the animateTo(0,0) method to get the list to scroll to the top. I found this only worked using anitmatTo(0,0) scrollTo(0,0) didn't seem to work.
Something along the lines of:
mListView.post(new Runnable(){ public void run() { mListView.animateScrollTo(0,0) });
Now as everyone has already stated you shouldn't do the whole ListView inside a ScrollView, but this may be a fix for the problem you had.
I have:
ScrollView
TextView
Button
TextView
ListView
and this work good for me:
scrollView.smoothScrollBy(0, 0);
without this view start from position of listview, after that it start from top
The solution for this issue is to make a request focus to an object in the top of the ScrollView. For example you can use a table layout wrapping the button and request focus to the table layout (If you focus the button it will change its color).
// onCreate method
TableLayout tablelayout = (TableLayout) findViewById(R.id.tablelayout);
tablelayout.setFocusableInTouchMode(true);
tablelayout.requestFocus();
Related
I want to make my Scrollview always scrollable even though it has no element in it.
I tried to extend a LinearLayout out of the screen, but it does not work.
csv=(ScrollView) findViewById(R.id.scrView);
csv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int scrollY= csv.getScrollY();
txtview.setText(String.valueOf(scrollY));
}
I want it because I want to see the image position which is the only one element in the ScrollView.
By this, I can move the image and see its y position.
It can be thought as vertical ViewPager.
Before Scrolling:
During Scrolling:
What I expect during scrolling:
I have a problem with HorizontalScrollView. When I choose an element from this View, I set focus to that element by calling:
else if (v.getParent() == candidatesScrollView.getChildAt(0))
{
Button candidateButton = (Button) v;
v.requestFocusFromTouch();
v.setSelected(true);
(...)
}
After that, when I scroll the list without choosing other element, I lose focus of previously selected element. I made some research about this topic, but there was no solution that could work for me... How can I scroll my HorizontalScrollList without loosing focus from selected element? Any Help is Appreciated. It has been about 14 days since I asked that question and still didn't find solution. Please help.
Here is part of my XML:
<HorizontalScrollView
android:id="#+id/CandidatesHorizontalScrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/linearLayout2"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:visibility="gone" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:orientation="horizontal" >
<Button
android:id="#+id/horizontalscrollview1_button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:textSize="25sp" />
(...)
// 11 more buttons
</LinearLayout>
</HorizontalScrollView>
UPDATE
MY CURRENT SOLUTION #1 (not working correctly):
After scrolling, and then scrolling again (for example scrolling back), scrolling starts from selected element.
I created custom HorizontalScrollView class inside which I overridden onTouchEvent() method. I don't think this is optimal way of doing that, because in that case I have to do calculations every time I move even one pixel. for example, if I add toast.show() to the below method, it will try to show as many toast as many I moved pixels (If I move by 10 pixels, it will try to show 10 Toast). Anyway, it works for me and the selection and focus are being kept. Please help me modify this code to make finally a good answer for that known issue:
#Override
public boolean onTouchEvent(MotionEvent ev)
{
int i = 0;
Button button = null;
for (; i < 11; i++)
{
button = (Button)((LinearLayout)getChildAt(0)).getChildAt(i);
if(button.isSelected())
break;
}
super.onTouchEvent(ev);
button.setSelected(true);
button.requestFocusFromTouch();
return true;
}
To be sure that the above code will work, you need to have only one selected item in your HorizontalScrollView at a time, i.e when you press diferent button, you need to make the previous one setSelected(false)
MY CURRENT SOLUTION #2 (not working correctly):
Solution #2 that I tried to implement, thinking that first one is not elegant enough, involves usage of gesture detector. In my custom HorizontalListView class I have added the following code:
Constructor:
public MyHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
gestureDetector = new GestureDetector(context, new MyHorizontalScrollViewGestureDetector());
this.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
return true;
}
});
// TODO Auto-generated constructor stub
}
MyHorizontalScrollViewGestureDetector internal class:
public class MyHorizontalScrollViewGestureDetector extends SimpleOnGestureListener
{
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{
//Here code similar like that one in solution #1
//But the View is not scrolling, even without that code
super.onScroll(e1, e2, distanceX, distanceY);
return true;
}
}
However, the list is not scrolling with that solution. I can add to onScroll method:
ScrollBy((int)positionX, (int)positionY);
which makes the list will scroll, but not in a good way ad it will freeze sometimes.
I am wondering why scrolling is not called by the super. method.
MY CURRENT SOLUTION #3 (working, but it is walk-around):
Because both solution 1 and 2 were not working, I decided to not play with focus anymore.
What I do now, is to change the Button Drawable whenever I click it and every time when I change to different Button. I use same Drawable as is used for focused button (Holo). In that case, I don't have to be worried about scrolling in HorizontalScrollView. This solution is some kind of walk-around, so I am looking forward to any comments, suggestions and edits.
Can the following solution be applicable (I took the idea from here):
You may want to try creating your own custom class that extends HorizontalScrollView and overriding the onScrollChanged() function as such:
public class TestHorizontalScrollView extends HorizontalScrollView {
public TestHorizontalScrollView(Context context) {
super(context);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
// TODO Auto-generated method stub
Log.i("Scrolling", "X from ["+oldl+"] to ["+l+"]");
Button button = null;
for (; i < 11; i++)
{
button = (Button)((LinearLayout)getChildAt(0)).getChildAt(i);
if(button.isSelected())
break;
}
button.setSelected(true);
button.requestFocusFromTouch();
super.onScrollChanged(l, t, oldl, oldt);
}
}
Now that you have detected the scroll (we're speaking about one of the HorizontalScrollView) you can set again the selected status of the corresponding button. Can you please try this solution out and see if it works. I'm very interested in the resolution of this one, as the question is quite interesting also.
Anyway, I managed to find the following article. They state there that:
Imagine a simple application, ApiDemos for example, that shows a list
of text items. The user can freely navigate through the list using the
trackball and they can also scroll and fling the list using their
finger. The issue in this scenario is the selection. If I select an
item at the top of the list and then fling the list towards the
bottom, what should happen to the selection? Should it remain on the
item and scroll off the screen? In this case, what would happen if I
then decide to move the selection with the trackball? Or worse, if I
press the trackball to act upon the currently selected item, which is
not shown on screen anymore. After careful considerations, we decided
to remove the selection altogether.
In touch mode, there is no focus and no selection. Any selected item
in a list of in a grid becomes unselected as soon as the user enters
touch mode. Similarly, any focused widgets become unfocused when the
user enters touch mode. The image below illustrates what happens when
the user touches a list after selecting an item with the trackball.
Anyway, the statements there didn't make me happy and I went further to find this:
android:focusable="true"
android:focusableInTouchMode="true"
Also set android:clickable="true" (you might as well set android:focusable="true" of the LinearLayout).
Try adding those to the buttons and to the Layout that contains them.
Try everything.
I'l try backing you up as much as I can.
Cheers
I remember having a problem with scroll views that I think was similar to the problem you're having.
The solution I came up with was to override the onRequestFocusInDescendants method in the HorizontalScrollView class. This requires you creating your own class extended from HorizontalScrollView if you aren't already doing so.
In my case, I always returned true from the method. This tells the caller that you have taken care of the focus change so it shouldn't try to do anything further.
#Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
return true;
}
Depending on your requirements, you may find it necessary to return true only under certain conditions (say when a scroll is in progress), otherwise forward the call to the superclass.
This question has no answer except for custom way of doing it, as you might have already done like changing the drawables. Reason is when you slide, the focus goes to the HorizontalScroll, and you can only focus on one item, it makes no sense to focus more than one view. So either implement a drawable (which you seem to have done) or extend a checkbox and override the functionality so when it is focused it is checked and changes the looks.
I have found a solution to my problem. You can see the updated question for details. Basically, instead of using focus I decided to use Drawable selector, which is much more easier.
i`m looking for way to position expandableListView group on top, when expanded.
I tried onGroupExpand, or in performItemClick of ExpandableListView.
setSelectedPositionFromTop(int, int), partialy works, but if it starts in a time when system starts it's own scroll, than list is overscrolled, and group is out of screen.
Sorry for messy description, but it's hard to say without showing exactly what i need.
Change transcriptmode to disabled.
android:transcriptmode="disabled"
in expandable list layout.
Put this in your adapter:
public void onGroupExpanded(final int groupPosition) {
super.onGroupExpanded(groupPosition);
listView.setSelectedGroup(groupPosition);
}
Try setSelection(groupPosition) in onGroupExpand(int groupPosition).
I tried many solutions but the below code works for me.Hope this code will be helpful for some one.Implements the OnGroupExpandListener with in onGroupExpand
use the below code
public void onGroupExpand(final int groupPosition) {
super.onGroupExpand(groupPosition);
expandableListView.post(new Runnable() {
#Override
public void run() {
expandableListView.setSelection(groupPosition);
if(expandableListView.getChildAt(groupPosition)!=null)
expandableListView.requestChildRectangleOnScreen(expandableListView.getChildAt(groupPosition),
new Rect(0, 0, expandableListView.getChildAt(groupPosition).getRight(), expandableListView.getChildAt(groupPosition).getHeight()), false);
}
});
}
I have solution to this problem. It's not gentle one, but it works quite fine.
Expandable listView messures is it should move by child number. It seems, that system thinks that child is same height as parent(group), so if you put different size child it will cause problems with auto scrolling list.
My solution was to trick list to think that it has more children.
If my child is twice as big as group, i just return information that group has 2 children. Now, since it is data constructed list, i make check on my data, is there a data for 2 childs, or only 1, if i don't have data for second child, i know that i can put 0 height view as second child.
It worked fine when i had to make List with small group, and than with 1 big child.
Since there was no answer to my question, i post this one, it may help someone.
I am having a listview inside a scrollview, but the problem is that the scrollview is scrolling but listview is not scrolling. I think this is due to that scrollView.
Can somebody who has a working solution post it here as reference?
Generally, you cannot put scrollable things inside other scrollable things, where they scroll in the same direction, and have the results be reliable. Occasionally this works (e.g., WebViews in a ViewPager), but that is the exception, not the norm.
Either:
Move the ListView out of the ScrollView, or
Move all the rest of the contents of the ScrollView into the ListView, whether using things like addHeaderView() or my MergeAdapter
If you put your ListView/any scrollable View inside the scrollView it will not work properly because when you touch the screen ,main focus of your touch is on parent view(scrollView ) not the child View (ListView).
ListView must have fixed height as below in your XML file
<ListView android:id="#+id/lv"
android:listSelector="#0f0"
android:layout_width="fill_parent"
android:layout_height="500px" />
In Java file, write below code after setContentView()
lv = (ListView)findViewById(R.id.lv);
lv.setAdapter(your adapter here); // you have to add your adapter here
lv.setOnTouchListener(new ListView.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE)
{
lv.scrollBy(0, 1);
}
return false;
}
});
Make these changes to your code and test it. After too many experiments i written this code. It is working 100% fine.
I have a ScrollView which I'm adding a number of custom Views to. After adding the custom Views, I would like to be able to Scroll to a particular one. But, immediately after adding the custom views, the ScrollView hasn't been resized, so I can not scroll. Any idea how I can force this resize?
for(int i = 0, i < numberOfViews, i++)
{
scrollView.addView(new MyView(...));
}
//tried this, doesn't seem to be helpful
scrollView.requestLayout();
//at this point scrollView dimensions are 0,0,0,0
scrollView.scrollTo(5, 200);
I'm not completely sure what you're trying to do, but I wanted to scroll all the way down, and couldn't just do that: I had to do it like this:
((ScrollView) findViewById(R.id.yourId)).post(new Runnable() {
public void run() {
((ScrollView) findViewById(R.id.yourId)).fullScroll(View.FOCUS_DOWN);
}
});
It will update later, so your view is made :)
An elegant solution is to plan the scroll and do it on the next onLayout(). Example code here:
https://stackoverflow.com/a/10209457/1310343