I was looking at material design website when I got to this page about circular progressbar and more precisely this video
I'd like to create a button like the one in the video but I don't see any documentation about this, and I don't know where to start
I've managed to simply use:
val progress = CircularProgressDrawable(context).apply {
setStyle(CircularProgressDrawable.DEFAULT)
// do use setBounds if you need to
start()
}
myMaterialButton.setCompoundDrawablesWithIntrinsicBounds(null, progress, null, null)
// Do your stuff
//replace the image by the one you want
myMaterialButton.setCompoundDrawablesWithIntrinsicBounds(null, myDrawable, null, null)
Reference: setCompoundDrawablesWithIntrinsicBounds
You could also use IndeterminateDrawable from material progress indicator. I see the CircularProgressDrawable animation breaks in API level 25
private val progressDrawableBackward by lazy {
val progressIndicatorSpec = CircularProgressIndicatorSpec(
context,
null,
0,
R.style.Widget_MaterialComponents_CircularProgressIndicator_ExtraSmall
)
progressIndicatorSpec.indicatorColors =
intArrayOf(progressColor)
IndeterminateDrawable.createCircularDrawable(context, progressIndicatorSpec).apply {
val size = (DRAWABLE_CENTER_RADIUS + DRAWABLE_STROKE_WIDTH ).toInt() * 2
setBounds(0, 0, size, size)
setVisible(true, true)
}
}
Related
I´d like to use icons in the text of a TextView. (Not in front or in the end of it.) The basic idea is a short infobox, telling the user, that these fancy three lines up there are the menu. (Since it is not possible to write "Menu" below them, except I want to handle all the click events myself too. sigh)
So my text should be something like this:
Right now this is just an icon added at the end of the textview, but as you can clearly see, it looks very ugly, and only works with a fixed screen-size. In landscape mode the icon would be at the completely other side of the screen, than the text.
Therefore I am searching for some way to write icons inside of the text.
I was thinking about something like "Um das Menü zu öffnen, tippe auf #drawable/sandwich" in the string resources or similar. (Which obviously doesn´t work like that, sadly.)
Is this possible? Or, if not, is there maybe some secret trick to add a text to the sandwich icon in the action bar at the top, without creating my custom layout?
Around 50% of my users have issues realizing it is a menu, since they are not used to a lot of apps.
Yes, you can do it using SpannableString. See the below example:
val modifiedText = "your-text-here %icon%" // you can use resource string here
val span = SpannableString(modifiedText)
val drawable = ResourcesCompat.getDrawable(resources, R.drawable.your_image, null)
drawable?.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
val image = ImageSpan(drawable, ImageSpan.BOTTOM)
val startIndex = modifiedText.indexOf("%icon%")
//Replace %icon% with drawable
span.setSpan(image, startIndex, startIndex + 6, Spannable.SPAN_INCLUSIVE_INCLUSIVE)
yourTextView.setText(span)
You could use VerticalImageSpan which is an extension to ImageSpan.
And use the following TextView extension function takes in the index in the TextView that you want to place the drawable in; a drawable, the desired image width & height.
fun TextView.addDrawableAt(index: Int, #DrawableRes imgSrc: Int, imgWidth: Int, imgHeight: Int) {
val ssb = SpannableStringBuilder(this.text)
val drawable = ContextCompat.getDrawable(this.context, imgSrc) ?: return
drawable.mutate()
drawable.setBounds(
0, 0,
imgWidth,
imgHeight
)
ssb.setSpan(
VerticalImageSpan(drawable),
index - 1,
index,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
)
this.setText(ssb, TextView.BufferType.SPANNABLE)
}
So I need to set up 2 things:
Icon for helper text in TextInputLayout
Center it to TOP, since helper text might be multiline
It might look something like this:
Is there easy of doing working around this, without making my own TextInputLayout?
Solving this melts down to following 2 possible solutions:
First is to change the Rect of the Drawable itself by setting the bounds (coordinates X and Y), drawback of this solution is whenever layout is redrawn (orientation change etc.), this has to be done again.
findViewById<AppCompatTextView>(R.id.textinput_helper_text)?.run {
compoundDrawables.first()?.run {
setBounds(
bounds.left,
bounds.top - this#with.height.div(2),
bounds.right,
bounds.bottom - this#with.height.div(2)
)
bottom = this#with.height.div(2)
}
}
Second solution is to add new ImageView in front of the helper TextView and add padding to it, so it's pushed up.
It's not a one liner, but it can be formed into easy to use method as such:
fun TextInputLayout.setStartHelperTextIcon(
#DrawableRes drawableRes: Int,
paddingPx: Int
) {
with(findViewById<AppCompatTextView>(R.id.textinput_helper_text)) {
(this?.parent?.parent as? LinearLayout)?.run {
if(findViewById<ImageView>(R.id.text_input_layout_helper_icon) == null) {
addView(
ImageView(context).apply {
id = R.id.text_input_layout_helper_icon
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
setPadding(
paddingPx,
0,
paddingPx,
paddingPx + this#with.height.div(2)
)
},
0
)
}
}
}
}
According to the official Design docs for Action Chips, we are supposed to be able to add a progress state to chips. Sadly, the Development docs don't mention this at all. Has anyone managed to figure out how to achieve the effect shown here?
It is possible to implement this behavior using the chipIcon attribute and androidx.swiperefreshlayout.widget.CircularProgressDrawable.
xml
app:chipIconWithProgress="#{viewModel.taskIsStarting ? null : #drawable/ic_play_arrow}"
and BindingAdapter
#BindingAdapter("chipIconWithProgress")
fun Chip.setChipIconWithProgress(item: Drawable?) {
chipIcon = item
?: CircularProgressDrawable(context!!).apply {
setStyle(CircularProgressDrawable.DEFAULT)
start()
}
}
You can use the Material Components Library and the Chip component.
In your layout use:
<com.google.android.material.chip.Chip
android:id="#+id/chip"
style="#style/Widget.MaterialComponents.Chip.Action"
app:iconStartPadding="2dp"
../>
Then use as chipIcon a ProgressIndicator provided by the library:
ProgressIndicatorSpec progressIndicatorSpec = new ProgressIndicatorSpec();
progressIndicatorSpec.loadFromAttributes(
this,
null,
R.style.Widget_MaterialComponents_ProgressIndicator_Circular_Indeterminate);
progressIndicatorSpec.circularInset = 1; // Inset.
progressIndicatorSpec.circularRadius =
(int) dpToPx(this, 8); // Circular radius is 8 dp.
IndeterminateDrawable progressIndicatorDrawable =
new IndeterminateDrawable(
this,
progressIndicatorSpec,
new CircularDrawingDelegate(),
new CircularIndeterminateAnimatorDelegate());
Chip chip = findViewById(R.id.chip);
chip.setChipIcon(progressIndicatorDrawable);
with this util method:
public static float dpToPx(#NonNull Context context, #Dimension(unit = Dimension.DP) int dp) {
Resources r = context.getResources();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
}
You can customize the colors and all other attributes:
progressIndicatorSpec.indicatorColors = getResources().getIntArray(R.array.progress_colors);
progressIndicatorSpec.growMode = GROW_MODE_OUTGOING;
Note: this requires at least the version 1.3.0-alpha02.
I am trying to better understand how layer drawables work within a buttons drawable(s).
I am trying to draw 2 simple colored boxes, one without insets so that it fills the entire button drawable area. And one with some inset.
ColorDrawable background1 = new ColorDrawable(Color.BLUE);
ColorDrawable background2 = new ColorDrawable(Color.GREEN);
Drawable[] drawables = new Drawable[] {
background1,
background2
};
LayerDrawable ld = new LayerDrawable(drawables);
ld.setLayerInset(0, 0, 0, 0, 0 ); // no inset on white box
ld.setLayerInset(1, 8, 8, 8, 8 ); // 8 inset on all side on green box
// set the left drawable on the button
button.setCompoundDrawablesWithIntrinsicBounds(ld, null, null, null);
However that doesn't work at all. The first problem is that the boxes are not filling any area. Is that because a buttons drawables(s) don't have a predefined size? If that is the case I tried to set the bound manually on the boxes, but didn't have much luck either.
Can anyone help me understand what I am doing wrong?
Create a Specific Design in Drawable and call in background button in xml
The problem right now with ColorDrawable is that getIntrinsicWidth()/getIntrinsicHeight() that determine the bounds of the drawable are by default -1, thus it doesn't render on the screen. What would help in your case is to extend ColorDrawable and override the height and width from the constructor.
Here's a sample:
class CustomDrawable(color: Int, val h: Int, val w: Int): ColorDrawable(color) {
override fun getIntrinsicHeight(): Int {
return h
}
override fun getIntrinsicWidth(): Int {
return w
}
}
and then you can instantiate this instead of ColorDrawable like so
val background1 = CustomDrawable(Color.BLUE, dimensionInPx , dimensionInPx)
val background2 = CustomDrawable(Color.GREEN, dimensionInPx, dimensionInPx)
be sure to convert dp to px before passing these values
Hope this helps.
I would like to make a graph like this :
The problem is I don't know how to set a gradient color like this using MPAndroidChart. Maybe I should use an other library ?
Maybe it's better to use progressbar with transparent color (and gradient background) ?
This is my code :
val entries = ArrayList<BarEntry>()
entries.add(BarEntry(1.toFloat(), 43.toFloat()))
entries.add(BarEntry(2.toFloat(), 3.toFloat()))
entries.add(BarEntry(3.toFloat(), 13.toFloat()))
entries.add(BarEntry(4.toFloat(), 41.toFloat()))
entries.add(BarEntry(5.toFloat(), 22.toFloat()))
entries.add(BarEntry(6.toFloat(), 11.toFloat()))
entries.add(BarEntry(7.toFloat(), 13.toFloat()))
entries.add(BarEntry(8.toFloat(), 99.toFloat()))
entries.add(BarEntry(9.toFloat(), 67.toFloat()))
entries.add(BarEntry(10.toFloat(), 3.toFloat()))
entries.add(BarEntry(11.toFloat(), 56.toFloat()))
entries.add(BarEntry(12.toFloat(), 88.toFloat()))
val dataSet = BarDataSet(entries, "Label")
chart.data = BarData(dataSet)
This worked for me:
dataset.setGradientColor(Color.parseColor("#00FF5722"),Color.parseColor("#FFFF5722"));
You can use setGradientFill() method which will accept n number of color.
int[] colors = { getResources().getColor(R.color.menu_text),
getResources().getColor(android.R.color.white) };
float[] index = { 0, 1 };
dataset.setGradientFill(colors, index);
for more you can refer this. Hope it will help you!!