In our application we need an indeterminate progress bar, like so:
We can achieve this by setting a negative margin on the ProgressBar, like this:
<ProgressBar
android:id="#+id/progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:marginTop="-7dp"
android:visibility="#{loading ? View.VISIBLE : View.GONE}" />
BUT because ConstraintLayout does not support negative margins, it will look like this:
OK, the negative margin was a hack. Let's replace it with a different hack, shall we? Let's introduce our custom view CustomProgressBar, which extends ProgressBar and overrides its onDraw method, like this:
#Override
protected void onDraw(Canvas canvas) {
int marginTop = dpToPx(7);
canvas.translate(0, -marginTop);
super.onDraw(canvas);
}
But all of this smells like bad code. There has to be a better solution!
What would you recommend?
Solution that feels less like a hack: Wrap a huge ProgressBar in a smaller FrameLayout. That way the FrameLayout constrains its height, but the ProgressBar still shows in full.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="4dp">
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="32dp"
android:layout_gravity="center" />
</FrameLayout>
Another way to do this is to use a Guideline and center ProgressBar between the parent top and the guideline.
<ProgressBar
android:id="#+id/progressBar2"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:paddingTop="-4dp"
android:layout_height="wrap_content"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toTopOf="#+id/guideline"
app:layout_constraintTop_toTopOf="parent" />
<android.support.constraint.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/guideline"
android:orientation="horizontal"
app:layout_constraintGuide_begin="4dp" />
I encountered the same problem as well. And like you said, I come across numerous solutions which all seem like a hack that might break something else down the line.
With that said, I came across one solution which is to use this library instead for progress bar.
One thing to note is, it tells you to integrate it by adding:
compile 'me.zhanghai.android.materialprogressbar:library:1.3.0'
However, when I used this, it gave me an error from an Android support library for Floating Action Bar. So I'll recommend you to use this instead:
compile 'me.zhanghai.android.materialprogressbar:library:1.1.7'
A sample code snippet on how I used it:
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="#+id/reviewProgressBar"
style="#style/Widget.MaterialProgressBar.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="6dp"
android:layout_below="#+id/my_toolbar"
android:indeterminate="false"
app:mpb_progressStyle="horizontal"
app:mpb_useIntrinsicPadding="false"/>
Hope this helps!
A dirty workaround I did was set the height of the ProgressBar closely to the to stroke width like so:
Progress bar:
<ProgressBar
android:id="#+id/pb_loading_progress"
style="#style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="2.1dp"
android:layout_below="#id/tb_browser"
android:max="100"
android:progressDrawable="#drawable/style_browser_progress_drawable"
android:visibility="invisible" />
Progress drawable:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#android:id/background">
<shape android:shape="line">
<stroke
android:width="2dp"
android:color="#color/colorPrimary" />
</shape>
</item>
<item android:id="#android:id/progress">
<clip
android:clipOrientation="horizontal"
android:gravity="left">
<shape android:shape="line">
<stroke
android:width="2dp"
android:color="#color/white" />
</shape>
</clip>
</item>
</layer-list>
Which looked like this:
You declared your view without any explicit height so it's height is being picked from the pre-defined style.
<ProgressBar
android:id="#+id/progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:marginTop="-7dp"
android:visibility="#{loading ? View.VISIBLE : View.GONE}" />
"?android:attr/progressBarStyleHorizontal" will be searched in current theme and is stored in attrs.xml file. Since this defined by the platform it will obtain it's the value from there. As of writing this answer, I'm on android platform 29 and searching "?android:attr/progressBarStyleHorizontal" gives the following results
➜ values
pwd
/Users/vihaanverma/Library/Android/sdk/platforms/android-29/data/res/values
➜ values
grep -rin "progressBarStyleHorizontal" .
./themes_device_defaults.xml:126: <item name="progressBarStyleHorizontal">#style/Widget.DeviceDefault.ProgressBar.Horizontal</item>
./themes_device_defaults.xml:857: <item name="progressBarStyleHorizontal">#style/Widget.DeviceDefault.Light.ProgressBar.Horizontal</item>
./themes.xml:272: <item name="progressBarStyleHorizontal">#style/Widget.ProgressBar.Horizontal</item>
./themes_holo.xml:263: <item name="progressBarStyleHorizontal">#style/Widget.Holo.ProgressBar.Horizontal</item>
./themes_holo.xml:626: <item name="progressBarStyleHorizontal">#style/Widget.Holo.Light.ProgressBar.Horizontal</item>
./public.xml:148: <public type="attr" name="progressBarStyleHorizontal" id="0x01010078" />
./themes_material.xml:258: <item name="progressBarStyleHorizontal">#style/Widget.Material.ProgressBar.Horizontal</item>
./themes_material.xml:635: <item name="progressBarStyleHorizontal">#style/Widget.Material.Light.ProgressBar.Horizontal</item>
./styles_material.xml:1010: <item name="progressBarStyle">?attr/progressBarStyleHorizontal</item>
./attrs.xml:684: <attr name="progressBarStyleHorizontal" format="reference" />
➜ values
Opening one of the files which contain "progressBarStyleHorizontal" you will see minHeight defined in the style and is equal to 16dp.
<style name="Widget.Material.ProgressBar.Horizontal" parent="Widget.ProgressBar.Horizontal">
<item name="progressDrawable">#drawable/progress_horizontal_material</item>
<item name="indeterminateDrawable">#drawable/progress_indeterminate_horizontal_material</item>
<item name="minHeight">16dip</item>
<item name="maxHeight">16dip</item>
</style>
This is the cause of the extra padding you are getting. You can fix it by giving your view height and a scaleY value like below
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="5dp"
android:indeterminate="true"
android:scaleY="5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Use inside Linearlayout and you will success, the simple way
<LinearLayout
android:layout_width="match_parent"
android:background="#efefef"
android:layout_height="wrap_content">
<ProgressBar
android:id="#+id/progressBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"
android:layout_marginTop="-7dp"
android:layout_marginBottom="-7dp"
style="#style/Widget.AppCompat.ProgressBar.Horizontal"
/>
</LinearLayout>
Related
This is how the button looks right now:
android:background= "#color/white" does not change anything.
Code:
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:text="Button"
android:background="#color/white"/>
Use app:backgroundTint="#FFFFFF"
If you want to use a selector, you will need to have android:background="#drawable/your_button_selector_id" as the button background for the selector to work.
Selector:
<selector xmlns:android="schemas.android.com/apk/res/android">
<item android:drawable="#color/white" android:state_selected="true" />
<item android:drawable="#color/black" android:state_selected="false" /> </selector>
Since you are using a color just use the app:backgroundTint attribute:
<Button
app:backgroundTint="#color/white"/>
If you just need rounded corners with a stroke, you don't need a drawable.
For it use:
<com.google.android.material.button.MaterialButton
app:cornerRadius="16dp"
app:backgroundTint="#color/white"
app:strokeColor="#color/black"
android:textColor="#color/black"
app:strokeWidth="2dp" />
Did you add value for white in your colors.xml?
if not open colors.xml and add
<color name="white">#FFFFFF</color>
Full Explanation here
https://stackoverflow.com/a/2749027/7174681
How can I make this? It looks nice and I'd like to use something like this.
The lines at the top should correspond to the end of the title
Actually, these types of view are mostly done with custom views.
This tutorial is useful for custom views.
But you can cheat a little bit, create backgound.xml inside the drawable folder, and paste the code:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:color="#color/blue" android:width="4dp"/>
<corners android:radius="10dp"/>
<solid android:color="#color/white"/>
</shape>
then create custom_background.xml for your layout, and paste the following code
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="300dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<LinearLayout
android:background="#drawable/background"
android:layout_marginTop="15dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:textSize="20sp"
android:textAlignment="center"
android:textColor="#color/blue"
android:layout_gravity="center"
android:text="Something"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
<TextView
android:padding="2dp"
android:textColor="#color/blue"
android:textSize="25sp"
android:background="#color/white"
android:layout_marginStart="50dp"
android:text="Title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Then you will get the following picture
Note
I am using androidX
Instead of LinearLayout you can place any layout
1) Migrate your project to androidx.
2) Include dependency in your build.gradle:
implementation 'com.google.android.material:material:1.0.0'
3) Use in your layout:
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_height="wrap_content"
android:hint="#string/hint_text">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
4) Don't forget to change your Base Application Theme AppTheme to Material Component theme.
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
You need to use material component theme!!
I'm using this library for VerticalSeekBars in my Android application. In my app, I'm laying multiple vertical seekbars horizontally (like about 10-15?) using LinearLayout and layout_weight properites. In addition, I'm using a thumb that is supposed to be bigger than the progress (essentially laying on top of the progress) and a custom drawable for the progress as such:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#android:id/background"
android:height="10dp"
android:gravity="center_vertical">
<shape android:shape="rectangle">
<corners android:radius="500dp"/>
<solid android:color="#000000"/>
</shape>
</item>
</layer-list>
On API 23 (which I believe is Marshmallow?), everything lines up correctly and resizing properly across different screen sizes. Awesome. However, on API 22 and below, the progress drawable doesn't adjust itself; instead, it increases it size to have the seekbar thumb fit inside the progress. I've tried to resize the drawable in my code but that still doesn't do anything either. I have drawn it out to show what I mean:
How do I get API 22/below to match the same behavior API 23 is achieving with the seekbar?
Here's an example of my VerticalSeekBar setup:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1">
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="0.5">
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
android:layout_width="0dp"
android:layout_height="0dp"
android:max="100"
android:progress="50"
app:seekBarRotation="CW270"
android:progressDrawable="#drawable/custom_track"
android:splitTrack="false"/>
</com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper>
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="0.5">
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
android:layout_width="0dp"
android:layout_height="0dp"
android:max="100"
android:progress="50"
app:seekBarRotation="CW270"
android:progressDrawable="#drawable/custom_track"
android:splitTrack="false"/>
</com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper>
</LinearLayout>
Thanks!
Set maxHeight like this:
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
...
android:maxHeight="2dp"
.../>
I want to have a gap in a border which is around a LinearLayout. In this gap there should be text.
I think the image explains it very well:
Is this possible? I know how to create a border around the Layout but not how to do this gap with the text. I hope someone can help me.
Make my_drawable.xml file in your project's res/drawable folder:
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="2dp"
android:color="#000066" />
<corners
android:radius="10dp" />
<solid
android:color="#android:color/white"/>
</shape>
After it you can add it as a background for a LinearLayout:
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="300dp" <!-- Or whatever you want -->
android:layout_height="200dp" <!-- Or whatever you want -->
android:background="#drawable/my_drawable">
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:layout_marginLeft="20dp"
android:textColor="#android:color/black"
android:textStyle="bold"
android:paddingLeft="10dp"
android:paddingRight="10dp"/>
</RelativeLayout>
I'm trying to do a bottom-level menu like this:
I've defined the layout this way:
<LinearLayout
android:id="#+id/bottomBar"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginTop="4dp"
android:orientation="horizontal"
android:layout_alignParentBottom="true" >
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fillViewport="true"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- First item of the menu -->
<ImageButton
android:id="#+id/BConex1"
android:contentDescription="#string/desc_gen_image"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.2"
android:layout_gravity="left"
android:background="#drawable/menu_bottom"
android:layout_marginLeft="4dp" />
<!-- Second item of the menu -->
<ImageButton
android:id="#+id/BConex2"
android:contentDescription="#string/desc_gen_image"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.2"
android:layout_gravity="left"
android:background="#drawable/menu_bottom"
android:layout_marginLeft="4dp" />
<!-- ... Some additional items in the menu -->
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
The #drawable/menu_bottom is a layer-list, defined this way:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="1px"
android:color="#55ffffff" />
<padding
android:left="16px"
android:top="6px"
android:right="16px"
android:bottom="6px" />
<solid
android:color="#cc000000" />
<gradient
android:startColor="#222222"
android:centerColor="#111111"
android:endColor="#000000"
android:angle="-90" />
<corners
android:bottomRightRadius="10dp"
android:bottomLeftRadius="10dp"
android:topLeftRadius="10dp"
android:topRightRadius="10dp" />
</shape>
</item>
<item>
<bitmap
android:src="#+drawable/bitmap_to_change"
android:tileMode="disabled"
android:width="30dp"
android:height="15dp"
android:gravity="center" />
</item>
</layer-list>
Now the thing is: I want to use exactly the same background for each button in the menu, in exception of the src field of the bitmap. I've tried to use LayoutInflater on one of the buttons and tried to change it programatically, like this:
View first = LayoutInflater.from(this).inflate(R.drawable.menu_bottom, null);
first.findViewById(R.drawable.bitmap_to_change).setBackground(this.getResources().getDrawable(R.drawable.another_resource));
But this returns an exception:
12-11 11:35:53.556: E/AndroidRuntime(897): java.lang.RuntimeException:
Unable to start activity ComponentInfo{XXX.MainActivity}:
android.view.InflateException: Binary XML file line #2: Error
inflating class layer-list
So I assume that's not the correct way. Is there a way to accomplish this instead of defining one XML file per menu item?
Thanks!
----------- EDIT -----------
I've found a "dirty" workaround to achieve my goal: getting the background of one of the buttons as a LayerDrawable and substituting the Bitmap with the correct. This is not the way I want to do it (as I want it entirely programatically) nor it's the original question, so I'm not going to answer myself letting the question opened in the hope someone can help, but here's what I'm doing now:
I assigned an id to the corresponding "item" in the XML file:
<item android:id="#+id:bitmap_layer">
And programatically:
ImageButton firstButton = (ImageButton) findViewById(R.id.BConex1);
LayerDrawable bg = (LayerDrawable) firstButton.getBackground();
bg.setDrawableByLayerId(R.id.bitmap_layer, getResources().getDrawable(R.drawable.the_new_drawable));
You don´t need to use the iflater, getting the Drawable resurse is enough:
android.graphics.drawable.Drawable shape = getResources().getDrawable(R.drawable.item_panel_borde);
with your workaroud you can get trouble if you use this drawable resource in other views, you can create your LayerList programatically using the android.graphics.drawable.LayerDrawable(Drawable[]) constructor.