I have several Activities in my Android app that have the same basic structure, and I'm trying to make my layouts DRY. The duplicated code looks like the below. It contains a scrollable area with a footer that has "Back" and "Dashboard" links. There's also a FrameLayout being used to apply a gradient on top of the scrollable area.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<ScrollView
android:layout_width="match_parent"
android:layout_height="689px">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- THE REAL PAGE CONTENT GOES HERE -->
</LinearLayout>
</ScrollView>
<ImageView
android:src="#drawable/GradientBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="50px"
android:background="?attr/primaryAccentColor">
<Button
android:layout_width="wrap_content"
android:layout_height="26px"
android:layout_gravity="center_vertical"
local:MvxBind="Click GoBackCommand" />
<Button
android:layout_width="wrap_content"
android:layout_height="26px"
local:MvxBind="Click ShowDashboardHomeCommand" />
</FrameLayout>
</LinearLayout>
To de-dupcliate my Activities, I think what I need to do is create a custom ViewGroup inherited from a LinearLayout. In that code, load the above content from an XML file. Where I am lost is how to get the child content in the Activity to load into the correct spot. E.g. let's say my Activity now contains:
<com.myapp.ScrollableVerticalLayoutWithDashboard
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- THE REAL PAGE CONTENT GOES HERE -->
<TextView android:text"blah blah blah" />
</com.myapp.ScrollableVerticalLayoutWithDashboard>
Now how do I cause the "blah blah blah" to appear in the correct place? I'm pretty sure if I did this, I would either end up with "blah blah blah" at the top or bottom of the page, not in the middle of the ScrollView as desired.
I'm using API 21 / v5.0+. Technically I'm doing all this with Xamarin, but hopefully that's irrelevant to the answer?
EDIT: An example of what the result would look like is this. The footer and gradient are part of the custom ViewGroup, but the rest would be content within the custom ViewGroup.
I don't know Xamarin so this is an native android solution, but should be easy to translate.
I think what I need to do is create a custom ViewGroup inherited from
a LinearLayout.
Yes, you could extend the LinearLayout class.
Where I am lost is how to get the child content in the Activity to
load into the correct spot.
In your custom implementation you need to handle the children manually. In the constructor of that custom class inflate the layout manually:
private LinearLayout mDecor;
public ScrollableVerticalLayoutWithDashboard(Context context, AttributeSet attrs) {
super(context, attrs);
// inflate the layout directly, this will pass through our addView method
LayoutInflater.from(context).inflate(R.layout.your_layout, this);
}
and then override the addView()(which a ViewGroup uses to append it's children) method to handle different types of views:
private LinearLayout mDecor;
public void addView(View child, int index, ViewGroup.LayoutParams params) {
// R.id.decor will be an id set on the root LinearLayout in the layout so we can know
// the type of view
if (child.getId() != R.id.decor) {
// this isn't the root of our inflated view so it must be the actual content, like
// the bla bla TextView
// R.id.content will be an id set on the LinearLayout inside the ScrollView where
// the content will sit
((LinearLayout) mDecor.findViewById(R.id.content)).addView(child, params);
return;
}
mDecor = (LinearLayout) child; // keep a reference to this static part of the view
super.addView(child, index, params); // add the decor view, the actual content will
// not be added here
}
In Xamarin you're looking for the https://developer.xamarin.com/api/member/Android.Views.ViewGroup.AddView/p/Android.Views.View/System.Int32/Android.Views.ViewGroup+LayoutParams/ method to override. Keep in mind that this is a simple implementation.
EDIT: Rather than putting a LinearLayout inside a LinearLayout, you could just use the 'merge' tag. Here's the final layout you'd want:
<?xml version="1.0" encoding="utf-8" ?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="#+id/svfFrame1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<ScrollView
android:layout_width="match_parent"
android:layout_height="689px">
<LinearLayout
android:id="#+id/svfContentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="23px" />
</ScrollView>
<ImageView
android:src="#drawable/GradientBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
</FrameLayout>
<FrameLayout
android:id="#+id/svfFrame2"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="50px"
android:background="?attr/primaryAccentColor">
<Button
android:id="#+id/FooterBackButton"
android:layout_width="wrap_content"
android:layout_height="26px"
android:layout_gravity="center_vertical"
android:layout_marginLeft="24px" />
<Button
android:id="#+id/FooterDashboardButton"
android:layout_width="wrap_content"
android:layout_height="26px"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="24px" />
</FrameLayout>
</merge>
And here's the final working C# view for Xamarin based on that layout:
public class ScrollableVerticalLayoutWithDashboard: LinearLayout
{
public ScrollableVerticalLayoutWithDashboard(Context context, IAttributeSet attrs) : base(context, attrs)
{
LayoutInflater.From(context).Inflate(Resource.Layout.ScrollableVerticalFooter, this);
base.Orientation = Orientation.Vertical;
}
public override void AddView(View child, int index, ViewGroup.LayoutParams #params)
{
// Check to see if the child is either of the two direct children from the layout
if (child.Id == Resource.Id.svfFrame1 || child.Id == Resource.Id.svfFrame2)
{
// This is one of our true direct children from our own layout. Add it "normally" using the base class.
base.AddView(child, index, #params);
}
else
{
// This is content coming from the parent layout, not our own inflated layout. It
// must be the actual content, like the bla bla TextView. Add it at the appropriate location.
((LinearLayout)this.FindViewById(Resource.Id.svfContentLayout)).AddView(child, #params);
}
}
}
Related
I have one RelativeLayout that has TextView (first label) , EditText(for input), TextView (second label). I have this in at least 10 activities in my project. How I can extract view and make my own. So, if I want to change textSize , I will have to change it on just one place, not 10.
For example I would like to have this
<RelativeLayout
android:width="match_parent"
android:height="wrap_content"
>
<TextView
android:id="firstTextView"
...
android:text="I like">
<EditText
android:id="edittextColor"
hint="type some color here"
... >
<TextView
android:id="secondTextView"
...
android:text="car.">
</RelativeLayout>
So, I need something like this on a lot of place. What I would like to have is:
<MySpecialView
firstText="I like"
colorEditTextHint="type color here"
secondText="car"/>
Inflaters
Let's suppose that your RelativeLayout file is called reusable_layout. This means that you could access it as R.layout.reusable_layout (considering that you have this file stored in the layouts folder of your project).
In your usual override of onCreate() add these variables at the start: LayoutInflater inflater = getSystemService(LAYOUT_INFLATER_SERVICE);
RelativeLayout layout = inflater.inflate(R.layout.reusable_layout, null);
Afterwards, call setContentView(layout);
If you want to edit the children you can call layout.getChildAt(int childNumber); This would return you a View
An example of editing the first TextView child:
TextView tv = (TextView) layout.getChildAt(0);
tv.setText("Example String");
UPDATE:
Another way to do what you want!
Creating a custom view may do the job!
A good tutorial on these is included here: https://developer.android.com/training/custom-views/create-view.html#subclassview
I think all you need to know is included in that.
Another possibly useful source would be included here: how to add views inside a custom View?
Hope I helped,
-Daniel
You can create one common layout and include in all the 10 activities layout like this
common_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/label1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Label1"/>
<EditText
android:id="#+id/input1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/label1"
android:text="Input1"/>
</RelativeLayout>
activity_layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/common_layout"/>
<TextView
android:id="#+id/textinactivity_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Activity text"/>
</LinearLayout>
I hope this is what you wanted.
Although Android offers a variety of widgets to provide small and
re-usable interactive elements, you might also need to re-use larger
components that require a special layout. To efficiently re-use
complete layouts, you can use the include and merge tags to
embed another layout inside the current layout.
https://developer.android.com/training/improving-layouts/reusing-layouts.html
What about <include>
create you your_base_layout.xml and <include> it in any other xml in the place where you want to add it
your_base_layout.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/some_other_id">
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/button1" />
</LinearLayout>
<include
android:id="#+id/include_id"
layout="#layout/your_base_layout" />
example of usage: another_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/app_bg"
android:gravity="center_horizontal">
<include
android:id="#+id/include_id"
layout="#layout/your_base_layout" />
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
android:padding="10dp" />
...
</LinearLayout>
This is how you access views in it,
View includedLayout = findViewById(R.id.some_id_if_needed);
Button buttonInsideTheIncludedLayout = (Button) includedLayout.findViewById(R.id.button1); // if there is a button in your base layout that you included access like this
find great answers >here
You can define your own control with specified attributes.
Save ButtonPlus.java into your package.
e.g.
public class ButtonPlus extends Button {
public ButtonPlus(Context context) {
super(context);
}
public ButtonPlus(Context context, AttributeSet attrs) {
super(context, attrs);
CustomFontHelper.setCustomFont(this, context, attrs);
}
public ButtonPlus(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
CustomFontHelper.setCustomFont(this, context, attrs);
}
}
And you can use inside your layout XML file.
I have to include one layout in my application. So that I have used
<include
android:id="#+id/support_layout"
android:width="match_parent"
android:height="match_parent"
layout="#layout/support"/>
I have referenced this include tag in my java file using View.
View v = (View) findViewById(R.id.support_layout);
But at some point of my code I have to Hide this layout.
so that I used v.GONE
But it's not Hiding.
I want to reference those text and button attributes located in XML programatically.
How can I do that?
There is my support.xml:
<LinearLayout
android:id="#+id/support_layout"
android:width="match_parent"
android:height="match_parent">
<TextView
android:id="#+id/txt"
android:width="match_parent"
android:height="wrap_content"/>
<Button
android:id="#+id/btn"
android:width="match_parent"
android:height="wrap_content"
android:text="Button"/>
</LinearLayout>
Since <include/> is not a View type in android and visibility is the property of View, we can not access the visibility from included layout's reference.
However if you are using kotlin with view binding, we can get the reference of root of the included layout like binding.supportLayout.root which probably will be one of the View (ConstraintLayout, RelativeLayout, LinearLayout etc.)
Now we have reference of view means we can play with their visibility like below code.
binding.supportLayout.root.visibility = View.GONE
Hope you got the idea.
We need to see your actual implementation of hiding that View you mentioned.
But, straight from reading of your question, I presume that you've might do it the wrong way.
To hide or make a view invisible, use this:
yourView.setVisibility(View.INVISIBLE);
Bear in mind that this does not remove the view compeletly; it would still remain in your layout and you could get a reference to it or even try to manipulate it.
To remove it compeletly, use this instead:
yourView.setVisibility(View.GONE);
Now if you call this, yourView would be compeletly removed from the layout. You will no longer able to get a reference to it.
Put that view into a linearlayout and hide the linearlayout. It will work.
<LinearLayout
android:id="#+id/support_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<include
layout="#layout/support"
android:height="match_parent"
android:width="match_parent" /> </LinearLayout>
And don't forget writing Linearlayout instead of View.
Briefly, instead of
View v = (View) findViewById(R.id.support_layout);
Do this
LinearLayout v = (LinearLayout) findViewById(R.id.support_layout);
You can hide this "included" layout with calling setVisibility() :
v.setVisibility(View.GONE)
and show it later with calling :
v.setVisibility(View.VISIBLE)
To reference button and textview from support layout you can use findViewById method on your included View (I'm not sure but I think it's even not mandatory, you can call it directly on your activity's view) :
View supportLayout = (View) findViewById(R.id.support_layout);
Textview txv = (TextView) findViewById(R.id.txt);
Button btn = (Button) findViewById(R.id.btn);
(if it's not working try with : Button btn = (Button) supportLayout.findViewById(R.id.btn);)
-- FYI --
When you give attributs to include tags it override ones of the included layout (there support_layout LinearLayout) so you don't need to do that
you must use like this includedLayoutId.viewId.visibility = View.GONE in this case you can access to included view, now for example:
loading.xml
<com.airbnb.lottie.LottieAnimationView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:lottie_autoPlay="true"
app:lottie_fileName="loading.json"
app:lottie_loop="true" />
in fragment_a.xml :
<include layout="#layout/loading"
android:id="#+id/anim_loading"
android:layout_width="match_parent"
android:layout_height="#dimen/_80sdp"/>
and finally use it animLoading.loading.visibility = View.GONE
Thanks to the new ConstraintLayout.
This is how I do it with widget.Group
<include
android:id="#+id/bottom_bar_action"
layout="#layout/bottom_bar_back_action"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.constraintlayout.widget.Group
android:id="#+id/bottom_bar_group"
android:layout_width="0dp"
android:layout_height="0dp"
app:constraint_referenced_ids="bottom_bar_action" />
Then you can hide the include layout by doing binding.bottomBarGroup.visibility = View.GONE. Cheers
// 1 - copy this
<?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="match_parent"
android:orientation="vertical"
tools:context=".Add">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="gone"
android:onClick="onclick_gone_include"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="visible"
android:onClick="onclick_visible_include"/>
<LinearLayout
android:id="#+id/support_layout"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="100dp"
>
<include
layout="#layout/support"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
//code
//2 copy this to Add cliass
//this methods on click in Add class
public void onclick_gone_include(View view) {
View v = (View) findViewById(R.id.support_layout);//view is the v
v.setVisibility(View.GONE);
}
public void onclick_visible_include(View view) {
View v = (View) findViewById(R.id.support_layout);
v.setVisibility(View.VISIBLE);
}
//3 activity that included 'support activity'
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
tools:context=".Add"
android:gravity="center"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="textview1"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="textview2"
/>
</LinearLayout>
I currently have a listview that has a header and footer attached. I am now trying to animate the items within the list itself, but when I apply a LayoutAnimationController to to the listview, the header is also animated. Is there a way to apply the animation to the whole list without affecting the header?
I have currently already tried the solution at Can LayoutAnimationController animate only specified Views by making a LinearLayout subclass that checks for animatable, but the header is still animating in with the rest of the items.
public class AnimationAverseLinearLayout extends LinearLayout {
private boolean mIsAnimatable = true;
public AnimationAverseLinearLayout(Context context) {
super(context);
}
public AnimationAverseLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setAnimatable(boolean isAnimatable) {
if(!isAnimatable)
clearAnimation();
mIsAnimatable = isAnimatable;
}
#Override
public void startAnimation(Animation animation) {
if(mIsAnimatable) {
super.startAnimation(animation);
}
}
}
....
//in onCreateView
ListView listView = (ListView) v.findViewById(android.R.id.list);
listView.setLayoutAnimation(Animations.animateListView(listView.getContext()));
header = (com.yardi.hud.inspections.util.AnimationAverseLinearLayout) inflater.inflate(R.layout.fragment_case_search_header, null, false);
header.setAnimatable(false);
listView.addHeaderView(header);
...
SearchAdapter adapter = new SearchAdapter(results);
setListAdapter(adapter);
I had the same problem described above - the animation was being applied to the ListView header as well as list rows. I solved this problem with help from this answer. I subclassed the outermost View of my header (in my case a FrameLayout) and overrode the animation methods to take no action. For example:
list_header.xml:
<my.project.NonAnimatingFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
...
>
<!-- other views in your header -->
...
</my.project.NonAnimatingFrameLayout>
and
package my.project
public class NonAnimatingFrameLayout extends FrameLayout {
...
#Override
public void setAnimation(Animation animation) {
// do nothing
}
The animation method you need to override my vary depending on the type of your outermost view.
I try like this if you want just look this xml file,
i declare header and footer separate & Listview separate in this if you want you can Animate listview . header and footer will not get animate.
if you give header and footer Animate then it will also animate...
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Header aligned to top -->
<RelativeLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="#FC9"
android:gravity="center" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="Fixed Header"
android:textColor="#000"
android:textSize="20sp" />
</RelativeLayout>
<!-- Footer aligned to bottom -->
<RelativeLayout
android:id="#+id/footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#FC0"
android:gravity="center" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="Fixed Footer"
android:textColor="#000"
android:textSize="20sp" />
</RelativeLayout>
<!-- LIstview Item below header and above footer -->
<ListView
android:id="#+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/footer"
android:layout_alignParentLeft="true"
android:layout_below="#+id/header" >
</ListView>
</RelativeLayout>
Not really the best solution, but would it be an alternative to for exemple:
create a container view (LinearLayout with vertical orientation)
add your header view
add your ListView
add a footer view
I mean, header and footer would be independent of your list component and would be a quick fix. Isn't it? You'd still being able to control header/footer visibility, for example, just by setting visibility as View.GONE.
Also 1, is there any other method you could implement? I mean, canAnimate or something like that?
Also 2, how is your getView method implemented.
Just providing a quick example of what I've explained:
<!-- Scrollable container -->
<ScrollView
android:id="#+id/scrollContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Container view -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/list"
android:orientation="vertical">
<!-- Header view -->
<TextView
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/header_text" />
<!-- LinearLayout acting as ListView -->
<LinearLayout
android:orientation="vertical"
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Insert rows into the fake list -->
<TextView
android:id="#+id/row_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/row_item" />
</LinearLayout>
<!-- Footer view -->
<TextView
android:id="#+id/footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/footer_text" />
</LinearLayout>
</ScrollView>
I believe the problem got solved now :)
I have a TabHost with several tabs, which content is defined as a FrameLayout, which wraps a TextView, each one of them having a lot of text, more than it can be shown within the screen layout, so I had to enable vertical scrolling for every tab.
The main thing is that those tabs are created dynamically (programatically), so I have to specify all the options this way:
final SoftReference<TabHost> th = new SoftReference<TabHost>((TabHost) ((Activity) globvars.getContext()).findViewById(android.R.id.tabhost));
final TabSpec setContent = th.get().newTabSpec(tag).setIndicator(tabview).setContent(new TabContentFactory() {
public View createTabContent(String tag) {
view.setBackground(globvars.getContext().getResources().getDrawable(R.drawable.rounded_edges));
view.setPadding(25, 25, 25, 25);
view.setTextColor(Color.WHITE);
view.setLines(50);
view.setMaxLines(maxLines);
view.setEllipsize(TextUtils.TruncateAt.START);
view.setHorizontalScrollBarEnabled(false);
view.setVerticalScrollBarEnabled(true);
view.setMovementMethod(new ScrollingMovementMethod());
view.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
view.setVerticalFadingEdgeEnabled(true);
view.setGravity(Gravity.BOTTOM);
view.setOverScrollMode(1);
view.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 11);
view.setTypeface(Typeface.MONOSPACE);
return view;
}
});
Using this approach, I can scroll backwards and forwards indeed, but visually the scrollbar is not shown. I'm mean this bar:
Am I missing something? Does the scrollbar have to be defined by a customized drawable by imperative?
Thanks!
------ EDIT (12-31-2013) ------
I've been looking around and still can't find any solution to this. I've tried as many combinations of parameters as I was able to find, but no result. Particularly, I've tried this and also wrapping the FrameLayout within a ScrollView, but instead of showing a scrollbar at the TextView itself, the whole TextView is wrapped within a scrollbar and grows when buffer gets bigger and bigger, that's not what I want.
Any help appreciated!
------ EDIT (01-17-2014) ------
Well, at this point, I can assure I've tried any logical step (well, at least to me) to make this work and still can't! I clarify that I have about 7-8 additional activities and I have no trouble with scrolling in any of them. I just work with TabHost in this one, though. So I'm starting a bounty on this, because that's already endangering my mental sanity.
I'm posting my layout below:
<LinearLayout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/fondo_imagen"
android:orientation="vertical"
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=".MainActivity" >
<LinearLayout
android:id="#+id/TabContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="99"
android:orientation="vertical">
<TabHost
android:id="#+android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/TabLinearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Horizontal scrolling for the tab widget -->
<HorizontalScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
android:scrollbars="none">
<TabWidget
android:id="#+android:id/tabs"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</HorizontalScrollView>
<FrameLayout
android:id="#+android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp" />
</LinearLayout>
</TabHost>
</LinearLayout>
<!-- Some additional LinearLayouts that don't have anything to do with the tab widget -->
...
</LinearLayout>
------ EDIT (01-19-2014) ------
Ok, based on corsair992's answer, I could finally get this working. My real mistake was assuming that even the method that creates the tab (posted above) receives a TextView as parameter, working with a View in the TabSpec definition would be casting it to the TextView. So indeed, I wasn't aware I was actually setting the scrollbars on a View (didn't know the View class didn't provide a public programatic method to configure scrollbars neither), so I followed corsair992's advice and created a separate layout with this content:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/tabsContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="3dp"
android:background="#drawable/rounded_edges"
android:gravity="bottom"
android:padding="8dp"
android:scrollHorizontally="false"
android:scrollbarStyle="insideOverlay"
android:scrollbars="vertical"
android:textColor="#FFFFFF"
android:textSize="11sp"
android:typeface="monospace"
tools:ignore="SmallSp" />
So now, instead of calling the above method which sets all those attributes to the View, I simply inflate this layout and set the MovementMethod:
final TabSpec setContent = th.get().newTabSpec(tag).setIndicator(tabview).setContent(new TabContentFactory() {
public View createTabContent(String tag) {
// tabs_content is the layout above
final View ll_view = LayoutInflater.from(globvars.getContext()).inflate(R.layout.tabs_content, null);
final TextView view = (TextView) ll_view.findViewById(R.id.tabsContent);
view.setMovementMethod(new ScrollingMovementMethod());
return ll_view;
}
});
It appears that the base View class does not provide a public method for initializing scroll bars if they are not specifically enabled in an XML layout resource. However, there is no reason you can't define your tab content in an XML resource, enable vertical scroll bars by setting the android:scrollbars attribute to vertical, and inflate it from the TabContentFactory dynamically.
Something like this in your case:
public View createTabContent(String tag) {
Activity activity = (Activity) globvars.getContext();
TextView view = (TextView) LayoutInflater.from(activity).inflate(R.layout.mytabcontent,
(ViewGroup) activity.findViewById(android.R.id.tabcontent), false);
view.setMovementMethod(new ScrollingMovementMethod());
view.setText("My Text");
return view;
}
Is it possible to have a margin/padding for the ListView without having the margin applied to the header? I want my ListView to be like the Google Now layout. I've got everything working except for this little problem.
Question: Is it possible to not have layout parameters of the ListView apply to its header?
EDIT1:More info
The purple view is the header to the listView.
I have tried adding margin to the list item layout.This doesnt work either.
All i need is a way to apply margin to the listview so that it doesnt apply the margin to the header in the listView.
EDIT2:
My layouts
header.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:orientation="vertical" >
<View
android:layout_width="match_parent"
android:layout_height="#dimen/top_height"
android:background="#color/top_item" />
<View
android:id="#+id/placeholder"
android:layout_width="match_parent"
android:layout_height="#dimen/sticky_height"
android:layout_gravity="bottom" />
</FrameLayout>
root_list.xml
<LinearLayout>
<com.test.CustomListView
android:id="#android:id/list"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_height="wrap_content" /></LinearLayout>
list_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/card_background"
android:orientation="vertical" >
<TextView
android:id="#+id/textview_note"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/textview_note_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"/></LinearLayout>
I've removed uselsess layout properties here due to length of this post.
After much digging around i found that its is not posssible to decouple the listView layout params from its headers and the header is part of listView. So in order to get the desirred margin effect, After much looking around I found out that adding margin to your root of the layout in the list view item didnt work. So I added another dummy LinearLayout inside the existing root LinearLayout(a nested LinearLayout) and added the children inside the nested layout.Now setting margin to the inner layout gives the desired effect.
Code:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:layout_marginLeft="16dp"
android:background="#drawable/list_selector"
android:orientation="vertical" >
<TextView
android:id="#+id/textview_note"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/textview_note_date"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
In your header part, add the following tag:
android:layout_alignParentLeft="true"
this will solve your problem.
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, attrs, false);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
...
return result;
this is the code of LayoutInflater,as you can see ,if you add a root,it's not null,generated the layoutParams from root.
and then if the thrid param is false,there is only make temp.setLayoutParams(params) which params is generated by root!! and the temp is as return....
if attachToRoot is true,root will execute root.addView(temp, params),and then the root is as return..
if you just do LayoutInflater.from(this).inflate(R.layout.popup_choose_type, null);,and if you had set some attribute as paddingBottom,it will surprised you that is will not work!!because of loss LayoutParams!
As for how to solve it,you can see
here
you can do it by give margin in listview's layout file then you will get it the output.