I am building an app that uses a REST interface to fetch data. Usually, I would call setProgressBarIndeterminateVisibility(true); to let the user know the app is waiting for data. However, this seems to be deprecated:
Progress bars are no longer provided in AppCompat.
When looking at the GMail app, I see there is an indeterminate progressbar (loading icon) full screen, where the data is going to come.
How would I implement that correctly? Some of activities are extended from ListViewActivity and use the setEmptyView to show a simple TextView when no data is available. Some are more elaborate layouts.
I could think of using setVisibility(View.GONE), but wondering if this is the "right" way to do it.
Yes. That is the right way. Just layout everything where they should be on the XML and call setVisibility on your progressBar when necessary.
There're usually two ways of kwnoing "when" to call.
on your REST callbacks (e.g. onSuccess(), onStartLoading())
using a DataSetObserver on your adapter (which is exactly what the ListView does). Like the following code:
.
#Override
protected void onStart(){
super.onStart()
observer.onChanged();
adapter.registerDataSetObserver(observer);
}
#Override
protected void onStop(){
super.onStop()
adapter.unregisterDataSetObserver(observer);
}
private DataSetObserver observer = new DataSetObserver(){
#Override
public void onChanged() {
progress.setVisibility(adapter.getCount()==0?View.VISIBLE:View.GONE);
}
}
Wrap your content layout into FrameLayout. Add to FrameLayout something like this:
<LinearLayout
android:id="#+id/loading_progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible">
<ProgressBar
android:layout_width="50dp"
android:layout_height="50dp"
android:indeterminate="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="Loading data..."/>
</LinearLayout>
And then switch content / progress layers in code using .setVisibility(View.VISIBLE) / .setVisibility(View.INVISIBLE)
Related
Say I have a fragment that has three buttons and I want to reuse it for at least three activities, but I want those buttons to do different things for each activity. For example, in ActivityA, I want button1 to open Google Maps while in ActivityB, button1 goes to the music player. Is this possible or even the right way?
Of course you can. Just create an interface for the Fragment, let's say FragmentCallback, with your desired callback method, onButtonClick() for instance. In the onAttached() of your Fragment, cast the Activity to your new interface and store it in a variable private FragmentCallback callback;. Each Activity using this Fragment must implement this callback interface. Then call the callbacks onButtonClick() method in your Fragments onButtonClick() method. That's it - a very common pattern.
Yes you can, but you have to add more logic to your fragments and add some interfaces for each activity.
I don't recommend to do that, maybe you could reuse your layouts.
Is this possible?
It definetely is. You could just check which Activity is hosting your Fragment instance:
private void button1OnClick(){
/* could also use instanceof, BUT: if you have something like ActivityC extends ActivityA
then instanceof would evaluate to true for both */
if(getActivity().getClass().equals(ActivityA.class)) {
// do stuff
} else if(getActivity().getClass().equals(ActivityB.class)) {
// do another stuff
}
}
Is this the right way?
(attention opinionated answer)
It depends. If you have a complex and unique layout/functionality, I'd use different Fragments. If you have a simple layout with some buttons that just need to act differently in different Activities it is a good idea to reuse an existing Fragment class.
Yes you can!
if(getActivity() instanceOf ActivityA) {
//do stuff related to ActivityA
} else if(getActivity() instanceOf ActivityB) {
//do stuff related to ActivityB
}
Your activities have different logic, you can define the button logic in each of them and share the views in this way. You can use a fragment to accomplish this however you can be more direct by sharing a partial layout.
Create a partial layout called three_buttons.xml
three_buttons.xml
<LinearLayout>
<BUtton android:text="button 1"/>
<BUtton android:text="button 2"/>
<BUtton android:text="button 3"/>
</LinearLayout>
activity_a.xml
<LinearLayout>
<TextView android:text="I am A"/>
<include
android:id="#+id/three_buttons"
layout="#layout/three_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
activity_b.xml
<LinearLayout>
<TextView android:text="I am B"/>
<include
android:id="#+id/three_buttons"
layout="#layout/three_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
I have a list view that is populated through a string-array in the xml, not run time and I'm trying to set the background color of specific items in the list using:
listView.getChildAt(x).setBackgroundColor(Color.BLACK);
I need this to happen before it's visible, but it gives an error when I use it in onCreate() or onStart(), but works if I run it on a button press. I've tried searching for an answer but can't seem to find any event that happens late enough for it to work.
getView() would be the best place to do this, but you don't have access with the way you are doing this with android:entries=.... Instead, you can post a runnable to the message queue to change the color after layout occurs like the following:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.layout).post(new Runnable() {
#Override
public void run() {
findViewById(R.id.view).setBackgroundResource(android.R.color.holo_red_dark);
}
});
}
Here I have used a simple View for demonstration but the same technique should work for your ListView.
Here is the XML I used if you want to work with it:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="#+id/view"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#android:color/holo_blue_light" />
</LinearLayout>
Here's a link to post. The runnable kicks off before onStart(). So, if that's a requirement, then this way may not work for you.
I used Android-PullToRefresh from chrisbanes to show refresh progress in my app.
I load data in AsyncTask, so I start refreshing by calling mPullRefreshListView.setRefreshing();
in onPreExecute method
and stop refreshing by
calling mPullRefreshListView.onRefreshComplete();
in onPostExecute method.
That works fine. But the problem happens when network down. Because I won`t use AsyncTask when network down any more, instead I load local cache.
I don`t want to show the PullToRefresh animations when refreshing if network down, but since it registered the setOnRefreshListener, every time when user pull down the ListView, it shows the refreshing animations, and never got disappear.
I tried to disable the animation by calling mPullRefreshListView.onRefreshComplete();, but it does`t help.
if (!Utils.isNetworkOn()) {
if (mPullRefreshListView.isRefreshing())
mPullRefreshListView.onRefreshComplete();
}
I feel very confused about this, anyone knows how to handle this?
Thanks in advance.
It is better to use SwipeRefreshLayout since Chris Bane's project is no longer maintained:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/srl_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
In fragment:
private void pullToRefreshForConfiguredList(View view) {
mConfiguredSwipeRefreshLayout = (SwipeRefreshLayout) view
.findViewById(R.id.srl_layout);
mConfiguredSwipeRefreshLayout
.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
// call Method to be refreshed
if (mConfiguredSwipeRefreshLayout.isRefreshing())
mConfiguredSwipeRefreshLayout.setRefreshing(false);
}
});
mConfiguredSwipeRefreshLayout.setColorScheme(android.R.color.black,
R.color.red, android.R.color.black, R.color.red);
}
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Android Activity Life Cycle
I am able to create a simple Splash Screen for my app using Themes and it works just fine. However, there may be times that the app needs to refresh a large amount of data and I want to display a ProgressBar to the user letting them know what's going on while they wait. So I ditched the Theme and created a layout resource and set that as the ContentView for my Splash Activity, but nothing displays. Not only does none of my content display (completely black screen) but the title bar continues to show up despite trying to hide it every way possible.
Splash.axml
<?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">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:src="#drawable/logo" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp" />
<TextView
android:id="#+id/progressText"
android:text="Loading..."
android:textSize="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp" />
</LinearLayout>
Splash.cs
[Activity(MainLauncher=true, NoHistory = true)]
public class Splash : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
RequestWindowFeature(WindowFeatures.NoTitle);
Window.SetFlags(WindowManagerFlags.Fullscreen, WindowManagerFlags.Fullscreen);
SetContentView(Resource.Layout.Splash);
string lastUpdated = PreferenceManager.GetDefaultSharedPreferences(this).GetString("LastInventoryUpdate", null);
DateTime lastUpdateDate = Convert.ToDateTime(lastUpdated);
TimeSpan span = DateTime.Today - lastUpdateDate;
if (string.IsNullOrEmpty(lastUpdated) || span.Days > 30)
{
TextView progressText = (TextView)FindViewById(Resource.Id.progressText);
progressText.Text = "Updating parts database. May take several minutes...";
InventoryRepository repository = ((MyApp)Application).Inventory;
repository.Execute();
}
StartActivity(typeof(Login));
}
}
Still shows logo and title in the title bar and absolutely nothing for content. Even tried taking all the AsyncTask and StartActivity stuff out and just tried loading the Activity with the splash layout. It does eventually show up, but it shows as the black screen for a long while first. Can't imagine why as this is my main launcher and there is literally nothing going on but setting the content of the activity.
And as far as the TitleBar showing up, I've also tried adding this to my manifest (which I currently have working in another activity just fine)
<activity android:name="app.MyApp.Splash" android:theme="#android:style/Theme.NoTitleBar.Fullscreen"></activity>
If I include a Theme for the activity, the TitleBar goes away, but my layout resource never displays.
Wouldn't you know after struggling all day I figured out a large part of the problem minutes after posting. In short, I put the Theme back on the Splash Activity which now shows while everything is initially loading and usually redirects to Login before the content is ever set, which is perfect. But I changed my OnCreate as follows:
[Activity(MainLauncher=true, NoHistory = true, ScreenOrientation = Android.Content.PM.ScreenOrientation.Landscape, Theme = "#style/Theme.Splash")]
public class Splash : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
RequestWindowFeature(WindowFeatures.NoTitle);
Window.SetFlags(WindowManagerFlags.Fullscreen, WindowManagerFlags.Fullscreen);
SetContentView(Resource.Layout.Splash);
string lastUpdated = PreferenceManager.GetDefaultSharedPreferences(this).GetString("LastInventoryUpdate", null);
DateTime lastUpdateDate = Convert.ToDateTime(lastUpdated);
TimeSpan span = DateTime.Today - lastUpdateDate;
if (string.IsNullOrEmpty(lastUpdated) || span.Days > 30)
{
SetTheme(Resource.Drawable.bg_black);
TextView progressText = (TextView) FindViewById(Resource.Id.progressText);
progressText.Text = "Updating parts database. May take several minutes...";
InventoryRepository repository = ((MyApp) Application).Inventory;
repository.Execute();
}
else
StartActivity(typeof (Login));
}
}
Main difference is that I only call StartActivity if I'm not running my AsyncTask. Otherwise, I call StartActivity from OnPostExecute of my AsyncTask. Also, be sure to set the background property of all your views in the splash layout. Since I left the theme on the activity, if you don't do this, your theme will show up as the background for EVERY view.
How do I create on-screen buttons for android apps. I am using the SDK's Lunar Lander game as a base, and I want to make it so that you don't need a keyboard.
To create a button you add something like this in your layout XML file:
<Button
android:id="#+id/button_1"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_margin="10dp"
android:text="Button 1"/>
To get a hook to the button in your code where you can add some action when the button is pressed add an OnClickListener:
findViewById(R.id.button_1).setOnClickListener(new MyButtonListener());
Declare the OnClickListener as a private class (or inline)
private class MyButtonListener implements OnClickListener {
#Override
public void onClick(View v) {
// Your code doing something cool goes here...
System.out.println("Click!");
}
}
Please look in the documentation that Mr Dittmar posted links to for more details, but this should hopefully get you started. :)
Take a look at the documentation on how to create a layout. After that, the documentation on how to create and handle buttons might come handy.