Is there any possibility to show a spinning progress bar in a button?
e.g. as background drawable?
I was having the same problem, so I created a specialized button for this: LoadingProgressButton
Include the button like this:
<br.com.simplepass.loading_button_lib.CircularProgressButton
android:id="#+id/btn_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/circular_border_shape"
app:spinning_bar_width="4dp" <!-- Optional -->
app:spinning_bar_color="#FFF" <!-- Optional -->
app:spinning_bar_padding="6dp" <!-- Optional -->
And use it like this:
CircularProgressButton btn = (CircularProgressButton) findViewById(R.id.btn_id)
btn.startAnimation();
[do some async task. When it finishes]
//You can choose the color and the image after the loading is finished
btn.doneLoagingAnimation(fillColor, bitmap);
[or just revert de animation]
btn.revertAnimation();
Yes.
You can create an AnimationDrawable, as described here, and then use the drawableLeft tag (for example) in your button's XML. like so:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/your_background_drawable_resource"
android:drawableLeft="#drawable/your_animation_drawable_resource"
android:text="#string/your_text_res">
</Button>
Yes... Just wrap around both the button and progressBar inside a Relative Layout, like so...
<?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"
android:weightSum="1">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/progressBar"
android:layout_gravity="right"
android:layout_alignTop="#+id/btnConnect"
android:layout_alignRight="#+id/btnConnect"
android:layout_alignEnd="#+id/btnConnect" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Connect"
android:id="#+id/btnConnect"
android:layout_gravity="center_horizontal"
android:layout_marginTop="30dp"
android:width="200dp"
android:focusable="false"
android:focusableInTouchMode="false" />
</RelativeLayout>
<TextView
android:id="#+id/txtConnectStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/btnConnect"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="Status : Not connected"
android:textSize="12dp"
android:layout_gravity="center_horizontal" />
<LinearLayout
android:orientation="vertical"
Wanted to post the sample image, but I don't have enough reputation just yet... ;)
I made a sample code like below..
I hope that my codes help you :)
[main.xml]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
>
<Button
android:id="#+id/wheel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/icon_spin_animation"
/>
</LinearLayout>
[icon_spin_animation.xml]
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/selected" android:oneshot="false">
<item android:drawable="#drawable/wheel_db_update01" android:duration="50"/>
<item android:drawable="#drawable/wheel_db_update02" android:duration="50"/>
<item android:drawable="#drawable/wheel_db_update03" android:duration="50"/>
<item android:drawable="#drawable/wheel_db_update04" android:duration="50"/>
<item android:drawable="#drawable/wheel_db_update05" android:duration="50"/>
<item android:drawable="#drawable/wheel_db_update06" android:duration="50"/>
</animation-list>
[Activity Code]
public class ProgressOnTheButtonActivity extends Activity implements OnClickListener {
/** Called when the activity is first created. */
AnimationDrawable mFrameAnimation = null;
boolean mbUpdating = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btnWheel = (Button)findViewById(R.id.wheel_button);
btnWheel.setOnClickListener(this);
mFrameAnimation = (AnimationDrawable) btnWheel.getBackground();
}
public void onClick(View v) {
if(v.getId() == R.id.wheel_button) {
if(!mbUpdating) {
mbUpdating = true;
new AsyncTaskForUpdateDB().execute("");
}
}
}
private class AsyncTaskForUpdateDB extends AsyncTask<String, Integer, ResultOfAsyncTask> {
#Override
protected void onPreExecute() {
mFrameAnimation.start();
super.onPreExecute();
}
#Override
protected ResultOfAsyncTask doInBackground(String... strData) {
ResultOfAsyncTask result = new ResultOfAsyncTask();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
#Override
protected void onPostExecute(ResultOfAsyncTask result) {
mFrameAnimation.stop();
mbUpdating = false;
}
#Override
protected void onCancelled() {
mFrameAnimation.stop();
mbUpdating = false;
super.onCancelled();
}
#Override
protected void onProgressUpdate(Integer... progress) {
}
}
private class ResultOfAsyncTask {
int iErrorCode = 0;
}
}
To make Animatable drawables work, you need to extend the Button class and call Animatable.start() for drawables. I've made an implementation for this:
package com.example.yourapplication;
import android.content.Context;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.AppCompatButton;
import android.util.AttributeSet;
public class AnimateCompoundDrawableButton extends AppCompatButton {
public AnimateCompoundDrawableButton(Context context) {
super(context);
}
public AnimateCompoundDrawableButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AnimateCompoundDrawableButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
super.setCompoundDrawables(left, top, right, bottom);
startIfAnimatable(left);
startIfAnimatable(top);
startIfAnimatable(right);
startIfAnimatable(bottom);
}
#Override
public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, Drawable bottom) {
super.setCompoundDrawablesRelative(start, top, end, bottom);
startIfAnimatable(start);
startIfAnimatable(top);
startIfAnimatable(end);
startIfAnimatable(bottom);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// Makes a copy of the array, however we cannot do this otherwise.
for (Drawable drawable : getCompoundDrawables()) {
startIfAnimatable(drawable);
}
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
// Makes a copy, however we cannot do this otherwise.
for (Drawable drawable : getCompoundDrawables()) {
stopIfAnimatable(drawable);
}
}
private void startIfAnimatable(Drawable drawable) {
if (drawable instanceof Animatable) {
((Animatable) drawable).start();
}
}
private void stopIfAnimatable(Drawable drawable) {
if (drawable instanceof Animatable) {
((Animatable) drawable).stop();
}
}
}
I have solved this problem by this way: first I have added progressBar and Button to common FrameLayout.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="75dp"
android:background="#color/white"
android:layout_marginRight="20dp"
android:layout_marginLeft="20dp">
<Button
android:id="#+id/buttonOnOff"
android:layout_width="match_parent"
android:layout_height="75dp"
android:text="Включить"
android:layout_gravity="center"
android:visibility="visible"
android:textColor="#FFFFFF"
android:onClick="onClickIn"
tools:ignore="OnClick" />
<ProgressBar
android:id="#+id/loading"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="center"
android:indeterminate="true"
android:indeterminateTintMode="src_atop"
android:theme="#style/ActionBarProgress"
/>
</FrameLayout>
And after that I have turned on and turned off visibility of progressBar and changed text of Button where I need it.
final Button buttonOnOff = findViewById(R.id.buttonOnOff);
final ProgressBar pB = findViewById(R.id.pB);
buttonOnOff.setEnabled(false);
buttonOnOff.setText("");
pB.setVisibility(false);
Another Option is to use the nifty Spezi-Views, it contains a ProgressButton which is quite easy to use:
<de.halfreal.spezi.views.ProgressButton
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press me"
app:selectedText="I am loaded"
app:unselectedText="Press me again"
app:loadingDrawable="#drawable/spinner"
/>
and in code:
...
//show a rotation spinner, and no text (or the loading text)
progressButton.enableLoadingState();
//show no animation, but the selected/ unselected text
progressButton.disableLoadingState();
...
With Jetpack Compose you can use something like:
var progressIndicatorVisible by remember { mutableStateOf(false) }
Button(onClick = {
scope.launch {
progressIndicatorVisible = true
// Just for example
delay(5000)
progressIndicatorVisible = false
}
}, modifier = Modifier.animateContentSize()){
if (progressIndicatorVisible) {
CircularProgressIndicator(
color = White,
strokeWidth = 2.dp,
modifier = Modifier.size(15.dp)
)
}
Text ("Button",
modifier = Modifier.padding(start = if (progressIndicatorVisible) 8.dp else 0.dp))
}
Related
I am making an Android application with Xamarin.Android and MvvmCross. I have a MvxRecyclerView:
<MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#null"
local:MvxItemTemplate="#layout/auto_complete_search_item"
local:MvxBind="ItemsSource SearchAutoCompleteItems; ItemClick SearchAutoCompleteItemClick" />
And my auto_complete_search_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="#dimen/auto_complete_search_item_height"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/margin_large"
local:MvxBind="Text Title" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/margin_large"
local:MvxBind="Text Category" />
</LinearLayout>
<TextView
android:id="#+id/fill_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="#dimen/text_medium"
android:textColor="#color/black"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
local:MvxLang="Text fa-arrow-up"
local:MvxBind="Style ., Converter=String, ConverterParameter=fonts/fontawesome.ttf" />
</LinearLayout>
My click handler ItemClick SearchAutoCompleteItemClick works, but I need to handle the click event for when the user clicks on my fill_button separately. How can I achieve this?
I figured out how to do this using a custom MvxRecyclerView and custom MvxRecyclerAdapter:
public class TwoPieceMvxRecyclerView : MvxRecyclerView
{
private ICommand _itemClickPiece1;
private ICommand _itemClickPiece2;
public ICommand ItemClickPiece1
{
get { return _itemClickPiece1; }
set
{
if (ReferenceEquals(_itemClickPiece1, value))
{
return;
}
_itemClickPiece1 = value;
}
}
public ICommand ItemClickPiece2
{
get { return _itemClickPiece2; }
set
{
if (ReferenceEquals(_itemClickPiece2, value))
{
return;
}
_itemClickPiece2 = value;
}
}
public TwoPieceMvxRecyclerView(Context context, IAttributeSet attr) : base(context, attr)
{
}
public TwoPieceMvxRecyclerView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
}
public class TwoPieceMvxRecyclerAdapter : MvxRecyclerAdapter, IOnClickListener
{
public TwoPieceMvxRecyclerAdapter(IMvxAndroidBindingContext context) : base(context)
{
}
public TwoPieceMvxRecyclerView TwoPieceMvxRecyclerView { get; set; }
protected override Android.Views.View InflateViewForHolder(Android.Views.ViewGroup parent, int viewType, MvvmCross.Binding.Droid.BindingContext.IMvxAndroidBindingContext bindingContext)
{
var view = base.InflateViewForHolder(parent, viewType, bindingContext);
var clickablePiece1 = view.FindViewById<View>(Resource.Id.clickable_piece1);
var clickablePiece2 = view.FindViewById<View>(Resource.Id.clickable_piece2);
clickablePiece1.SetOnClickListener(this);
clickablePiece2.SetOnClickListener(this);
return view;
}
public void OnClick(View v)
{
var viewHolder = this.TwoPieceMvxRecyclerView.FindContainingViewHolder(v);
var item = GetItem(viewHolder.LayoutPosition); // What different is viewHolder.AdapterPosition? I tested it with 100 items and it's always the same, but I'm not sure if this will never break...
if (v.Id == Resource.Id.clickable_piece1)
{
TwoPieceMvxRecyclerView.ItemClickPiece1.Execute(item);
}
else if (v.Id == Resource.Id.clickable_piece2)
{
TwoPieceMvxRecyclerView.ItemClickPiece2.Execute(item);
}
}
}
And then in the containing activity (or fragment):
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.activity_search);
var r = FindViewById<TwoPieceMvxRecyclerView>(Resource.Id.my_twopiecemvxrecyclerview);
var adapter = new TwoPieceMvxRecyclerAdapter(((IMvxAndroidBindingContext)BindingContext));
adapter.TwoPieceMvxRecyclerView = r;
r.Adapter = adapter;
}
And use it like this:
<My.Namespace.TwoPieceMvxRecyclerView
android:id="#+id/my_twopiecemvxrecyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#null"
local:MvxItemTemplate="#layout/my_two_piece_layout"
local:MvxBind="ItemsSource MyItems; ItemClickPiece1 MyItemClick1; ItemClickPiece2 MyItemClick2" />
my_two_piece_layout.xml needs to have views with ids clickable_piece1 and clickable_piece2
You can add a second binding to the TextView. That will bind a Click event to a command you will define in the SearchAutoCompleteItem type (the command will be declared on the level of the individual item).
local:MvxBind="Click FillCommand; Style ., Converter=String, ConverterParameter=fonts/fontawesome.ttf"
I'm trying to make a Floating Action Button Menu in my app. It should look like the FAB in the Google Calendar app, which is the kind of a active FAB shown by the Material Design Guideline:
See here:
https://material.io/guidelines/components/buttons-floating-action-button.html#buttons-floating-action-button-floating-action-button
Under "The floating action button changes color on focus and lifts upon being selected." there are two videos. The right one shows what this menu should look like. I have a menu like button that works perfectly but what I cannot achive is this gray background that blocks the whole UI (like an alert) with the action and task bar...
Normal:
And selected:
As you can see, the material design guideline tells us that a selected FAB should block the whole UI. But I don't know how...
So what I have at the moment:
I have a global_fab.xml that holds my two FABs:
<?xml version="1.0" encoding="utf-8"?>
<layout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:id="#+id/fab_background"
android:visibility="gone">
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/global_fab_mini"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="90dp"
android:layout_marginRight="24dp"
android:src="#drawable/ic_credentials_light"
android:theme="#style/Base.Widget.AppCompat.ImageButton"
android:visibility="gone"
app:backgroundTint="#color/cool_grey"
app:fabSize="mini" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/global_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_add_light"
android:theme="#style/Base.Widget.AppCompat.ImageButton"
app:fabSize="normal"
android:visibility="visible" />
</RelativeLayout>
</layout>
This FAB has a corresponding ViewGroup Class:
package de.mycompany.ui.view;
import android.content.Context;
import android.databinding.DataBindingUtil;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import com.mycompany.package.android.R;
import com.mycompany.package.android.databinding.GlobalFabBinding;
public class GlobalFABView extends FrameLayout implements IGlobalFABView {
private GlobalFabBinding globalFABViewBinding;
private Listener listener;
private boolean fabMenuOpen = false;
public GlobalFABView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public GlobalFABView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
globalFABViewBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.global_fab, this, false);
addView(globalFABViewBinding.getRoot());
this.globalFABViewBinding.globalFab.setOnClickListener(this::globalFabClicked);
this.globalFABViewBinding.fabBackground.setOnClickListener(this::backgroundClicked);
}
private void globalFabClicked(View v) {
this.listener.mainFabClicked();
}
private void backgroundClicked(View v) {
this.listener.backgroundClicked();
}
#Override
public void show() {
}
#Override
public void hide() {
}
#Override
public void setListener(Listener listener) {
this.listener = listener;
}
#Override
public void toggleFabMenu() {
Animation fabAnimation = AnimationUtils.loadAnimation(this.getContext(), this.fabMenuOpen ?
R.anim.global_fab_close :
R.anim.global_fab_open);
final boolean fabMenuOpen = this.fabMenuOpen;
if (!fabMenuOpen) {
globalFABViewBinding.globalFabMini.setVisibility(VISIBLE);
}
globalFABViewBinding.fabBackground.setVisibility(
fabMenuOpen ? GONE : VISIBLE
);
fabAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
// Nothing to do.
}
#Override
public void onAnimationEnd(Animation animation) {
if (fabMenuOpen) {
globalFABViewBinding.globalFabMini.setVisibility(GONE);
globalFABViewBinding.globalFab.setSelected(!fabMenuOpen);
}
}
#Override
public void onAnimationRepeat(Animation animation) {
// Nothing to do.
}
});
globalFABViewBinding.globalFabMini.startAnimation(fabAnimation);
this.fabMenuOpen = !fabMenuOpen;
}
}
My menu opens and closes without problems (I have two anim xmls for this). The Background RelativeLayout (+id/fab_background) is there to handle the "click anywhere and close menu" event.
Now I add this into any activty I want it:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:id="#+id/fab_holder">
<include layout="#layout/app_bar_with_progress_bar" android:id="#+id/appbar"/>
<de.mycompany.ui.view.HomeView
android:id="#+id/home_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/appbar" />
<de.mycompany.ui.view.GlobalFABView
android:id="#+id/global_fab_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
OK, I got this.
in order to lay a new layer over the whole screen we need to start a new activity that is transparent.
I created an activity that holds the menu. Into my normal activity I only added one Floating Action Button. In the click action of this, I open my MenuActivity without animations, so I can animate the appearance of the Floating Action Button Menu. This is my Activity Layout:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fab_background"
android:background="#color/fab_menu_background"
android:visibility="gone">
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/global_fab_mini"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="90dp"
android:layout_marginRight="24dp"
android:src="#drawable/ic_credentials_light"
android:theme="#style/Base.Widget.AppCompat.ImageButton"
android:visibility="gone"
app:backgroundTint="#color/cool_grey"
app:fabSize="mini" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/global_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_add_light"
android:theme="#style/Base.Widget.AppCompat.ImageButton"
app:fabSize="normal"
android:visibility="visible" />
<TextView
android:text="#string/fab_mini_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:id="#+id/fab_mini_label"
android:background="#drawable/fab_label_background"
android:gravity="center"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:layout_marginRight="4dp"
android:elevation="2dp"
android:textSize="14dp"
android:layout_alignBottom="#+id/global_fab"
android:layout_toStartOf="#+id/global_fab"
android:visibility="gone"/>
<TextView
android:text="#string/fab_main_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/fab_main_label"
android:background="#drawable/fab_label_background"
android:gravity="center"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:elevation="2dp"
android:textSize="14dp"
android:layout_marginBottom="41dp"
android:layout_above="#+id/fab_mini_label"
android:layout_alignStart="#+id/fab_mini_label"
android:visibility="gone" />
</RelativeLayout>
</layout>
In my Activity I have this function:
public void toggleFabMenu() {
Animation fabAnimation = AnimationUtils.loadAnimation(this.getContext(), this.fabMenuOpen ?
R.anim.global_fab_close :
R.anim.global_fab_open);
Animation labelAnimation = AnimationUtils.loadAnimation(this.getContext(), this.fabMenuOpen ?
R.anim.global_fab_labels_close :
R.anim.global_fab_labels_open);
Animation backgroundAnimation = AnimationUtils.loadAnimation(this.getContext(), this.fabMenuOpen ?
R.anim.global_fab_menu_background_close :
R.anim.global_fab_menu_background_open);
final boolean fabMenuOpen = this.fabMenuOpen;
if (!fabMenuOpen) {
globalFABViewBinding.globalFabMini.setVisibility(VISIBLE);
globalFABViewBinding.fabMiniLabel.setVisibility(VISIBLE);
globalFABViewBinding.fabMainLabel.setVisibility(VISIBLE);
globalFABViewBinding.fabBackground.setVisibility(VISIBLE);
}
fabAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
if (fabMenuOpen) {
globalFABViewBinding.globalFabMini.setVisibility(GONE);
globalFABViewBinding.fabMiniLabel.setVisibility(GONE);
globalFABViewBinding.fabMainLabel.setVisibility(GONE);
globalFABViewBinding.globalFab.setSelected(!fabMenuOpen);
}
}
#Override
public void onAnimationEnd(Animation animation) {
if (fabMenuOpen) {
globalFABViewBinding.fabBackground.setVisibility(GONE);
listener.menuDisappeared();
}
}
#Override
public void onAnimationRepeat(Animation animation) {
// Nothing to do.
}
});
globalFABViewBinding.globalFabMini.startAnimation(fabAnimation);
globalFABViewBinding.fabMiniLabel.startAnimation(labelAnimation);
globalFABViewBinding.fabMainLabel.startAnimation(labelAnimation);
globalFABViewBinding.fabBackground.startAnimation(backgroundAnimation);
this.fabMenuOpen = !fabMenuOpen;
}
In the animations I have fade animations for the labels and the background and a transition animation that moves my fab mini button to the place it belongs.
Now the trick to fit the background even over the status bar is, we cannot do it! So we have to find a workaround and this is not very obvious. First I thought to make the activity fullscreen but then the status bar disappears. So we have to let the status bar there but we need to make it transparent. So we see the color of the statusbar that lays behind it and can lay over our background. To do this I created a new theme:
<style name="Theme.Transparent" parent="AppTheme">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowContentOverlay">#null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:immersive">false</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowAnimationStyle">#null</item>
<item name="colorPrimaryDark">#android:color/transparent</item>
</style>
Yes. this is exactly what I want.
How can I add a progress bar in a PreferenceFragment? I have some async task running that will display some values in my preference upon completion. So while it is doing the background process, I plan to show only a progress bar in the center. After the task is complete, then I plan to show all my preferences.
Here's what I have so far.
PreferenceFragment
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_account);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
new AsyncTask<Void,Void,Void>() {
String firstName, lastName, email;
#Override
protected void doInBackground() {
// Doing something here to fetch values
}
#Override
protected void onPostExecute() {
findPreference("first_name").setSummary(firstName);
findPreference("last_name").setSummary(lastName);
findPreference("email").setSummary(email);
}
}
}
pref_account.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference android:key="first_name"
android:title="#string/prompt_first_name"
android:summary="" />
<Preference android:key="last_name"
android:title="#string/prompt_last_name"
android:summary="" />
<Preference android:key="email"
android:title="#string/prompt_email"
android:summary=""
android:inputType="textEmailAddress" />
<Preference android:key="sign_out"
android:title="#string/action_sign_out" />
</PreferenceScreen>
My question is, since this is a PreferenceFragment and I'm not overriding the method onCreateView, where and how should I add the progress bar?
Here's what I did:
Create a custom Preference class:
public class LoadingPreference extends Preference {
public LoadingPreference(Context context){
super(context);
setLayoutResource(R.layout.preference_loading_placeholder);
}
}
Create a custom Layout (preference_loading_placeholder) while preserving the Preference layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingEnd="?android:attr/scrollbarSize"
android:background="?android:attr/selectableItemBackground" >
<ImageView
android:id="#+android:id/icon"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dip"
android:layout_marginEnd="6dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
<!-- (start) I added this part. -->
<ProgressBar
android:id="#+id/progressBar"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateTintMode="src_atop"
android:indeterminateTint="#color/colorAccent"
android:layout_gravity="center" />
<TextView
android:id="#+id/progressTitle"
android:text="#string/loading"
android:textSize="24sp"
android:textColor="#color/colorAccent"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:layout_toLeftOf="#+id/progressBar" />
<!-- (end) I added this part. -->
<TextView android:id="#+android:id/title"
android:layout_width="0dp"
android:layout_height="0dp"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<TextView android:id="#+id/summary"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_below="#android:id/title"
android:layout_alignStart="#android:id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="4" />
</RelativeLayout>
<!-- Preference should place its actual preference widget here. -->
<LinearLayout android:id="#+id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical"/>
</LinearLayout>
Used a PreferenceCategory to hold my loading preference:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:key="#string/pref_key_wifi_enabled"
android:title="#string/pref_title_wifi_enabled"
android:summary="#string/pref_summary_wifi_enabled" />
<Preference
android:key="#string/pref_key_wifi_refresh"
android:title="#string/pref_title_wifi_refresh"
android:summary="#string/pref_summary_wifi_refresh"
android:dependency="#string/pref_key_wifi_enabled"/>
<PreferenceCategory
android:key="#string/pref_key_wifi_section"
android:title="#string/pref_title_wifi_section"
android:summary="#string/pref_summary_wifi_section"
android:dependency="#string/pref_key_wifi_enabled"/>
</PreferenceScreen>
Put the loading preference where needed
((PreferenceCategory)findPreference(WIFI_OPTIONS_SECTION_KEY)).addPreference(new LoadingPreference(getActivity()));
Remove the loading preference after loading is complete:
((PreferenceCategory)findPreference(WIFI_OPTIONS_SECTION_KEY)).removeAll();
Add other preferences after loading is complete:
Preference p = new Preference(getActivity());
p.setTitle("...");
p.setSumary("...")
((PreferenceCategory)findPreference(WIFI_OPTIONS_SECTION_KEY)).addPreferece(p);
I had a similar use case and I used a custom layout for the preference.
In the preference fragment file I had (non relevant attributes are omitted)
<!-- res/xml/somePref.xml -->
<PreferenceScreen>
<PreferenceCategory>
...
<Preference android:widgetLayout="#layout/customLayout" />
...
</PreferenceCategory>
</PreferenceScreen>
Then in the custom layout I placed the progress bar by itself
<!-- res/layout/customLayout.xml -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminateOnly="true" />
</RelativeLayout>
Before starting the async task I call
findViewById(R.id.progress).setVisibility(View.VISIBLE)
and when the task is done I set the visibility back to gone. This will allow you to set each preference independently without having to wait for each one to complete. Hope this helps.
I ended up using the following approach.
In my fragment_account.xml layout file.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="#+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</RelativeLayout>
Then on my class:
public class AccountFragment extends PreferenceFragment {
private ListView mListView;
private ProgressBar mProgressBar;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_account, container, false);
mListView = (ListView) view.findViewById(android.R.id.list);
mProgressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
showProgress();
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
addPreferencesFromResource(R.xml.pref_account);
hideProgress();
}
private void showProgress() {
mListView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
}
private void hideProgress() {
mListView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
}
}
You could try to apply the following solution:
Use fragments in your preference activity.
When you need to display preferences - display preference fragment.
When you need to display progress - display progress fragment.
Here is a code excerpt:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showPreferences();
// or call showProgress, launch your task and call showPreferences upon completion
}
private void showPreferences() {
runOnUiThread(new Runnable() {
#Override
public void run() {
getFragmentManager()
.beginTransaction()
.replace(android.R.id.content, new YourPreferenceFragment())
.commit();
}
});
}
private void showProgress() {
runOnUiThread(new Runnable() {
#Override
public void run() {
getFragmentManager()
.beginTransaction()
.replace(android.R.id.content, new YourProgressFragment())
.commit();
}
});
}
Use this approach. Create one Dialog with a custom layout which a progressBar in it and just call show() and dismiss().
#Synchronized
fun showLoader(activity: Activity) {
val activityWeakReference = WeakReference(activity)
if (activityWeakReference.get() != null) {
hideLoader()
dialog = Dialog(activityWeakReference.get()!!, android.R.style.Theme_Translucent)
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.setContentView(R.layout.progress_indicator)
dialog?.setCancelable(false)
dialog?.show()
}
}
#Synchronized
fun hideLoader() {
dialog?.dismiss()
dialog = null
}
Use the following concept in AsyncTasks default Methods
private class MyAsynTask extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
showDialog();// show your progressbar
}
#Override
protected String doInBackground(String... urls) {
//your code
}
protected void onProgressUpdate(String... progress) {
progressDialog.setProgress(Integer.parseInt(progress[0]));
// here you get the value of progress
}
#Override
protected void onPostExecute(String result) {
dismissDialog(); //dismiss progressbar
}
}
How can I add big AND small letters in a TextView like in the image below:
Sorry: "nuber" = "number"
It looks like the button you see in the UI isn't a standard Android Button, but rather a composite View made up of two TextViews.
You should make sure that your composite View is focusable and clickable, (not the two TextViews that its made of) and provide default, focus and pressed states for this View.
public class MyCompositeView extends LinearLayout {
private TextView primaryTextView;
private TextView secondaryTextView;
public MyCompositeView(Context context, AttributeSet attrs) {
super(context, attrs);
super.setOrientation(HORIZONTAL);
}
#Override
public final void setOrientation(int orientation) {
if (orientation != HORIZONTAL) {
throw new IllegalArgumentException("MyCompositeView is a button-like widget, not a ViewGroup! Don't change my orientation");
}
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
View.inflate(getContext(), R.layout.merge_my_composite_view, this);
primaryTextView = (TextView) findViewById(R.id.my_composite_text_primary);
secondaryTextView = (TextView) findViewById(R.id.my_composite_text_secondary);
}
public void setPrimaryText(String text) {
if (valid(text)) {
primaryTextView.setVisibility(VISIBLE);
} else {
primaryTextView.setVisibility(GONE);
}
primaryTextView.setText(text);
}
private static boolean valid(String text) {
return text != null && !text.trim().isEmpty();
}
public void setSecondaryText(String text) {
if (valid(text)) {
secondaryTextView.setVisibility(VISIBLE);
} else {
secondaryTextView.setVisibility(GONE);
}
secondaryTextView.setText(text);
}
}
The merge layout:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/my_composite_text_primary"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center" />
<TextView
android:id="#+id/my_composite_text_secondary"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center" />
</merge>
And you would use it as:
<?xml version="1.0" encoding="utf-8"?>
<com.example.MyCompositeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
You can try to use Html.fromHtml as:
button.setText(Html.fromHtml("html_code_with"));
instead of html_code_with set your html code for big text and small text.
I have been trying for a while now, Have looked at a few SO posts, and got to this stage. I am sure I am very close to getting it working, but am missing something very basic.
Code Snippets of the activity below
public class SearchResultsMap extends MapActivity {
....
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.searchresultsmap);
...
busy = (LinearLayout) findViewById(R.id.busy);
busy.bringToFront();
progressBar = (ProgressBar) findViewById(R.id.progressBar) ;
progressBar.bringToFront() ;
searchResponse = (HashMap<Integer, CustomJourneyUserInformation>) this.getIntent()
.getSerializableExtra("searchResponse");
new GetProviderAndUserDetails(this).execute(null);
>>edit1 progressBar.setMax(searchResponse.size());
>>edit1 progressBar.setProgress(0) ;
...
}
//Inner Class, type AsyncTask
public class GetProviderAndUserDetails extends AsyncTask<String, Integer, Object> {
public GetProviderAndUserDetails(Activity mapActivity) {
this.mapActivity = mapActivity;
}
protected void onPreExecute() {
busy.setVisibility(View.VISIBLE);
setProgressBarVisibility(true) ;
setProgressBarIndeterminate(true) ;
setProgressBarIndeterminateVisibility(true) ;
}
#Override
protected void onProgressUpdate(Integer... progress) {
if (searchActivity != null) {
searchActivity.setProgress(progress[0]);
>>edit1 progressBar.setProgress(progress[0]) ;
}
}
#Override
protected Object doInBackground(String... args) {
...
//for loop, i
publishProgress(i) ;
//end for loop
...
}
protected void onPostExecute(Object result) {
super.onPostExecute(result);
busy.setVisibility(View.GONE);
setProgressBarVisibility(false) ;
setProgressBarIndeterminate(false) ;
setProgressBarIndeterminateVisibility(false) ;
...
}
}
Layout XML File
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:id="#+id/busy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="visible" >
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:indeterminateOnly="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp" />
</LinearLayout>
.....
<com.google.android.maps.MapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/searchMap"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="#id/tip"
android:layout_alignParentTop="true"
android:apiKey="mykey"
android:clickable="true" />
....
</RelativeLayout>
Device Results : Unable to upload to stackoverflow. Some SO issue.
searchresults image
You're calling setProgressBarIndeterminate() and related functions, which act on your activity's title, not on the progress bar in your layout.
Use progressBar.setIndeterminate() and friends instead.
EDIT: And if you want a progress bar instead of the indeterminate progress indicator, use setIndeterminate(false).
Setting the style to progressBarStyleHorizontal in the xml worked finally. Seems like progressBarStyleLarge cant show progress.
Also the indeterminate thing helped.