I have a ScrollView that contains a complex LinearLayout with various elements, among which there are several TextView with maxHeight defined and a long text inside. I need to enable scrolling the text inside the TextView when touch-and-dragging inside it, and the normal scroll of the ScrollView when touch-and-drag outside of them.
Is there a proper and elegant way to do this - if at all possible - or do I have to get dirty and start overriding the onTouchEvent handlers on all elements involved?
UPD 1: Judging from this there is no elegant solution, and the UI will have to be rethinked to include only one layer of scrollable elements.
So it's not pretty, but you can extend from TextView and block the scrollview from scrolling. It's not as nice unless your textviews are large enough. After using this solution we found that it is best if you have a "show more" label that the users can click to show the full thing. Anyway, extend from TextView and override the onTouchEvent method:
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
this.getParent().requestDisallowInterceptTouchEvent(true);
}
if (ev.getAction() == MotionEvent.ACTION_UP) {
this.getParent().requestDisallowInterceptTouchEvent(false);
}
return super.onTouchEvent(ev);
}
A ScrollView is a FrameLayout, meaning you should place one child in it containing the entire contents to scroll; this child may itself be a layout manager with a complex hierarchy of objects. A child that is often used is a LinearLayout in a vertical orientation, presenting a vertical array of top-level items that the user can scroll through.
<ScrollView
android:id="#+id/ScrollView01"
android:layout_height="150px"
android:layout_width="fill_parent">
<TextView
android:id="#+id/TEXT_VIEW"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="This text view should act as header This text view should act as header This text view should act as header This text view should act as header This text view should act as header This text view should act as header This text view should act as header" />
</ScrollView>
you can see the APIdemos for more information, there is a a view of lot of text's with their own stylized scrollview in the same windows. For more inforation you can visit:
http://developer.android.com/resources/samples/ApiDemos/index.html
Related
I'm inflating views inside a linearlayout dynamically, however once the linear layout reaches the end of the first row, it cuts off the rest and doesn't start on the second row.
for(int a = 0; a < mSkills.get(i).size(); a++){
View singleSkill = LayoutInflater.from(mContext)
.inflate(R.layout.singleskill, holder.mSkillLayout, false);
TextView skillText = singleSkill.findViewById(R.id.singleskilltext);
skillText.setText(mSkills.get(i).get(a));
holder.mSkillLayout.addView(skillText);
}
For the linear layout I have it set to wrap_content for the height:
<LinearLayout
android:id="#+id/ll_skills"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_below="#+id/tv_description"
android:layout_margin="16dp"/>
I've tried setting it to a defined height e.g 300dp however that doesn't work either.
How can I make the layout start on the second row, once the first is full?
Linear Layout can either fill views horizontally or vertically so the 2nd row you are expecting cant to be done with linear layout only. you can try a horizontal scroll view for that to scroll horizontally. For the exact view-like flow that you described, you can use this 3rd party https://github.com/nex3z/FlowLayout
It can manage the flow of your dynamically inflated view such as if there is no space in the first line then it will put the next view in the second line.
also, you can use material design library chips https://material.io/components/chips/#usage
LinearLayout works exactly how it has to be because you specify it as horizontal. For such behavior, you need RecyclerView With GridLayoutManager or create your own layout;).
Actually it's doing exactly as it should be, LinearLayout is Linear!, and place its subviews in a single horizontal or vertical row.
My advice to you is that create dynamic horizontal LinearLayout as you already doing with TextViews. and put every 3 or 4 textviews (depending on screen size) inside it.
and put all LinearLayouts inside one vertical LinearLayout...
Of course in your case, it's not a good idea, the best thing you can do is to use recycler view. but I consider you have problem with that.
I spent a good part of a day looking into scrolling content in Android. However, in my app I need to apply a custom type of scrolling that contains among other things a listview. A listview has its own scrolling and a scrollview should never be used with a list view together.
But there must be some intrinsic scroll functionality for views. The documentation on View does indicate support but the methods provided do not seem to provide any listeners to detect scrolling.
A scrollview also is built on a FrameLayout which would also be problematic in my app.
You can make a LinearLayout scrollable with:
android:isScrollContainer="true"
but this doesn't seem to do anything.
In fact you can even set the scrollbars for a LinearLayout:
android:scrollbars="vertical"
but that also doesn't seem to do anything.
I don't want to control scrolling but rather have a listener that detects scrolling on the screen regardless whether a listview, framelayout or any other control is visible. I guess what I am looking for is writing my own custom ScrollView control but without the limitations of its FrameLayout and all the unnecessary overhead that I don't need. Any suggestions?
On way to resolve above problem is to have 2 ScrollView, one is the outer ScrollView that contain all the view and other inner Scrollview instead of listview and adding the items inside LinearLayout dynamically(Inside inner ScrollView)
Actually after doing some research, I come up with a solution for this problem:
At first I want to explain the problem in a very simple way.
1.LinearLayout will be scrollable.
2.To do this we can use ScrollView but sometimes we need to use ListView inside LinearLayout.
3.We know that inside ScrollView we cannot use another scrollview like ListView
How to solve that?
ListView is scrollable inherently so we can add header and footer in the ListView. As a conclusion:
1.Create Layout header.xml and footer.xml and list.xml
2.Find the ListView reference from list.xml in the main activity and dynamically add header and footer in the ListView reference.
Here is what seems like a viable solution which I tested out but not thoroughly. Create a custom control that extends a LinearLayout and then override the dispatchTouchEvent:
#Override
public boolean dispatchTouchEvent(MotionEvent event)
{
MotionEvent vtev = MotionEvent.obtain(event);
final int actionMasked = event.getActionMasked();
float x = event.getRawX();
float y = event.getRawY();
switch (actionMasked)
{
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
int z = 0;
z++;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_POINTER_DOWN:
{
final int index = event.getActionIndex();
break;
}
case MotionEvent.ACTION_POINTER_UP:
break;
}
boolean ret = super.dispatchTouchEvent(event);
return ret;
}
}
You can then place child controls inside this custom LinearLayout and detect motion events, even when ListViews and ScrollViews are present. This doesn't mess with their own scrolling.
I have a horizontal scroll view, I also have a relativelayout as it's child. I am adding child views of this relativelayout dynamically. I have a header text which should be update when I scroll according to respective child views. How can I do this because I am able to get the current focused item in horizontal scroll. Please give me some suggestion or examples which can be helpful for me, thanks..
You should specify an OnTouchListener for your HorizontalScrollView and in it's onTouch() method detect the type of MotionEvent and change your TextView's color to the appropriate
If you are creating this childs dinamically you can set a tag to them with the content you want to show in the header TextView.
//Creating RelativeLayout childs
TextView newChild = new TextView(this).
newChild.setTag(textToShowWhenThisItemIsFocused);
Then if you know which item is focused you just have to get the tag.
// "selected" is the focused view
header.setText((String) selected.getTag());
When to use the second code depends on your implementation. Since you didn't provide any code it's hard to know how to monitor the scroll, but i.e. you could control Touch Events and update the header when the user is moving his finger over the screen (you should also take into account the inertia after the user stops tapping).
EDIT: How to get the focused View
First of all, I barely have experience doing things like this, so I'm not sure if this is going to work or if there are better ways to do it. I'll just tell you the way I would approach this problem.
To be able to know the focused View you need to know the coordinates where this View should be. Since it's an horizontal ScrollView we will need the X coordinate. Since we want the View in the middle of the ScrollView I would do it like this:
private int centerSV;
private ScrollView mScrollView;
...
centerSV = mScrollView.getWidth()/2;
Now we have the center of the ScrollView. Now we need to know which child is in this position:
private int getFocusedChildId(){
for(int i=0; i<mChilds.length; i++){
int childLeftCoord = mChilds[i].getLeft() - mScrollView.getScrollX();
if(childLeftCoord <= centerSV && centerSV <= childLeftCoord + mChilds[i].getWidth())
return mChilds[i].getId();
}
// No view found in the center, maybe ScrollView wasn't full. Return the first one
return mChilds[0].getId();
}
Again, I'm not sure if this is going to work, it's just an idea of how to approach your issue. Also, you should take this into account:
getWidth() and getHeight() of View returns 0
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 quite complex View build-up, and as a part of that, I have a ListView inside a LinearLayout within a ScrollView (and quite a lot more components, but they do not matter in this issue).
Now the whole activity scrolls nicely as it should, but the ListView has a limited height, and when the Items inside it surpass the height, the disappear of my screen. I've tried to place the ListView inside it's own ScrollView, but this doesn't work. When I try to scroll on the ListView, the main ScrollView is selected and my screen scrolls instead of the ListView.
My question may sound easy, but I haven't been able to fix this... Is it possible to make the ListView scrollable aswell?
The relevant XML:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:id="#+id/GlobalLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView android:id="#+id/EndpointList"
android:choiceMode="multipleChoice"
android:layout_height="175dip"
android:layout_width="fill_parent" />
</LinearLayout>
</ScrollView>
Instead of ListView with other layouts inside ScrollView create one ListView with header and footer.
Add views that should be above ListView as a header:
addHeaderView(View v)
and that below as a footer:
addFooterView(View v)
Put everything what should be above ListView to header of ListView and the same with footer.
LayoutInflater inflater = LayoutInflater.from(this);
mTop = inflater.inflate(R.layout.view_top, null);
mBottom = inflater.inflate(R.layout.view_bottom, null);
list.addHeaderView(mTop);
list.addFooterView(mBottom);
// add header and footer before setting adapter
list.setAdapter(mAdapter);
In result you'll get one scrollable view.
Actually, the way that I have it set up is really working... Placing a ListView in a LinearLayout within a ScrollView. Just avoid that the ListView is the direct child of the ScrollView, and it will work out just fine...
Just be aware that if there aren't enough items in the ListView to fill it so it goes 'off screen', that it won't scroll (kind of logically though). Also note that when you have enough items to scroll through, you need to keep pressing on an item in the ListView to make it scroll, and half of the time, focus is given to the global scrollview in stead of the ListView... To avoid this (most of the time), keep pressing on the most top or most down item, depending on which way you want to scroll.This will optimize your chance to get focus on your ListView.
I've made a video that it is possible, am uploading it now to YouTube...
Video is http://www.youtube.com/watch?v=c53oIg_3lKY. The quality is kinda bad, but it proves my point.
Just for a global overview, I used the ScrollView to be able to scroll my entire activity, the LinearLayout for the Activity's layout, and the ListView to, well, make the list...
I would like to just note something for the video
The listview is working once every x touches not because you placed it inside a linearlayout but because you are touching the the divider...
the scrollview will then consider that the place you touched does not have children to dispatch the motionevent to... so it calls the super.dispatchTouchEvent which is in this case the View.dispatchTouchView hence the listview.onTouchEvent.
When you touch inside a row the scrollview which is really a viewgroup will send the dispatch to the children in your case the textview and never calls the one of the view so the listview do not scroll.
Hope my explanation was clear enough to point out why is it not working.