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);
}
Related
I have a fragment that contains swipeRefreshLayout and infinity recyclerView in it. My fragment layout code is similar to:
<androidx.constraintlayout.widget.ConstraintLayout
.
.
.
android:layout_height="match_parent"
android:layout_width="match_parent"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
.
.
.
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent" >
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_main_news"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
I set recyclerView's adapter in onViewCreated() and call notifyDataSetChanged() in onResume(). In some devices like Samsung Galaxy A5 , when user go back to fragment and notifyDataSetChanged called, recyclerView moves to top and show first item.
How can I prevent this?
Thanks in advance.
Update: This is my onResume() code that calls notifyDataSetChanged()
override fun onResume() {
super.onResume()
if (rv.adapter != null)
(rv.adapter as MyAdapter).apply {
fontSize = Options.getFontSize()
notifyDataSetChanged()
}
}
it's expected that when notifyDataSetChanged called entire view is refreshed. When you go back to the fragment onResume is called, subsequently your notifyDataSetChanged also called. That's why you see the first item.
So, to prevent this you need to call notifyDataSetChanged when your whole data is changed or need to be updated.
I can suggest you some way.
remove notifyDataSetChanged from onResume & use diffUtil for your recyclerView data update. It will update only your changed item. Update your data only when it's necessary, like if you update any data in details & want to update in list you can override onActivityResult & notify your list there. Or you can send broadcast & receive it in you list fragment & notify you list.
thanks in advance, hope this will help.
You can try save scrolling state of the RV when updating:
val recyclerViewState = rv.layoutManager?.onSaveInstanceState()
adapter.notifyDataSetChanged()
rv?.layoutManager?.onRestoreInstanceState(recyclerViewState)
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 would like to override the back button in an activity so that I can start a fragment, normally within activities I use an intent but as I understand I cannot use an intent to call fragments.
The reason I want to use the back button to start the intent is so it can reload new information from the database, otherwise, if I don't override the back button it will just load the previous fragment with old (cached) data.
My code below which does not work:
#Override
public void onBackPressed(){
handler.removeCallbacksAndMessages(null);
new leftRoom().execute();
Intent intent = new Intent(ActivityChatRoom.this,Fragment_01_Rooms.class);
startActivity(intent);
}
Update:
fragment_01_rooms.xml:
<?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:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- android:background="#color/gray_header"-->
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fadeScrollbars="false" >
</ListView>
</LinearLayout>
Update (activity_main.xml):
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/viewpager"
tools:context=".FragmentMain"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Try this:
#Override
public void onBackPressed(){
handler.removeCallbacksAndMessages(null);
new leftRoom().execute();
getSupportFragmentManager().beginTransaction().replace(R.id.viewpager, new Fragment_01_Rooms()).commit();
}
You would have to add a FrameLayout as the container in the activity if you don't already have a container view in which to inflate the fragment. Let me know if you need more clarifications.
EDIT
Thanks a lot for the full activity code. Since you are using a ViewPager, replacing a fragment this way won't work. Now, you mentioned two lines in the onBackPressed you wanted to implement:
handler.removeCallbacksAndMessages(null);
new leftRoom().execute();
What is the role of these two methods and where are they implemented? If these methods update your db, and if Fragment_01_Rooms is the leftmost element in your viewPager, I would suggest you to do this:
In the onResume of your Fragment_01_Rooms, refresh the data that you are going to show the user (if the two lines above already refresh the data, you don't need to do anything).
In the Activity, override onBackPressed and call viewPager.setCurrentItem(0) instead of getSupportFragmentManager...commit().
Let me know if this solves the problem.
#Override
public void onBackPressed(){
handler.removeCallbacksAndMessages(null);
new leftRoom().execute();
}
In your leftRoom AsyncTask onPostExecute call this.
getSupportFragmentManager().beginTransaction().replace(R.id.container, new Fragment_01_Rooms()).commit();
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)
I created a swipe refresh layout in my android app.
There is a ListView in the main activity fragment and when the user swipes the list view, the app fetches data from the internet to display in the listview.
My problem is that even after fetching the data (correctly), the swipe refresh wheel is not going away from the app.
Here is the code for my main fragment xml file:
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/activity_main_swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ListView
android:id="#+id/listview_forecast"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#00000000"
android:dividerHeight="4dp"/>
</android.support.v4.widget.SwipeRefreshLayout>
And here is the code for my Main activity fragment file (partial)
swipeContainer = (SwipeRefreshLayout) rootView.findViewById(R.id.activity_main_swipe_refresh_layout);
swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
Log.v(TAG, "Swiped!") ;
}
});
I created the refresh listener inside the onCreateView method of the Main activity fragment.
What can I do to correct it?
Once your task is finished call:
swipeContainer.setRefreshing(false);