How to make SwipeRefreshLayout work when the RecyclerView is empty - java

I noticed the SwipeRefreshLayout works when my RecyclerView is filled with data but not when the RecyclerView is empty. Is there a way to override this behavior?
My app fetches some data from the server and populates the RecyclerView. So for example, if the user has forgotten to turn on their internet but started my app, I want them to be able to turn it on and swipe up to refresh instead of going back and starting the activity again.
Here's is my activity's xml. I have removed some code to make this less verbose. I had one more button outside of the SwipeRefreshLayout and I have also removed my constraints.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BreakfastActivity"
android:orientation="vertical">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/b_swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="0dp">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_breakfast"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.constraint.ConstraintLayout>
And this is the .java file:
public class BreakfastActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
// to display the menu
RecyclerView rv_breakfast;
RecyclerView.Adapter my_adapter;
// the actual menu
ArrayList<Menu> menu;
private SwipeRefreshLayout swipeRefreshLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_breakfast);
rv_breakfast = findViewById(R.id.rv_breakfast);
swipeRefreshLayout = findViewById(R.id.b_swipe_refresh_layout);
swipeRefreshLayout.setRefreshing(true);
swipeRefreshLayout.setOnRefreshListener(this);
rv_breakfast.setLayoutManager(new LinearLayoutManager(this));
new GetMenu(this).execute();
}
#Override
public void onRefresh() {
new GetMenu(this).execute();
}
static class GetMenu extends GetMenuBase {
WeakReference<BreakfastActivity> context;
GetMenu(BreakfastActivity b) {
context = new WeakReference<>(b);
meal_type = "breakfast";
b.swipeRefreshLayout.setRefreshing(true);
}
#Override
protected void onPostExecute(JSONObject parent) {
// process the output of the server side script script
b.swipeRefreshLayout.setRefreshing(false);
}
}
The java file is again devoid of some code not concerning the question. GetMenuBase extends an AsyncTask and implements doInBackground() and makes a call to the server and returns the JSON output.

The problem is that when your RecyclerView is empty then your height will be 0dp because you've set the height to wrap_content and 0dp to your SwipeRefreshLayout.
Change the height of your SwipeRefreshLayout or your RecyclerView to match_parent.

Related

Using Butterknife with BaseActivity/Child Activities and MaterialDrawer

I'm facing issues implementing MaterialDrawer (library MikePenz) using Multiple Activities ( not using fragments) which is implemented with a BaseActivity. Also, using ButterKnife to bind the views.
Found related issues here in SO and tried it out one by one , but in my case it was not working since most of them using standard Navigation Drawer with Fragments or they are not related with ButterKnife.
Code :
MainActivity class
public class MainActivity extends AppCompatActivity {
#BindView(R.id.toolbar)
Toolbar toolbar;
private List<TestModel> destinations;
private DestinationAdapter mAdapter;
ProgressDialog progressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
navManagement();
}
}
activity_main layout
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/maincontainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tvs.ui.MainActivity">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay"
/>
</android.support.design.widget.AppBarLayout>
</android.support.constraint.ConstraintLayout>
StoreActivity
public class StoresActivity extends MainActivity {
#BindView(R.id.searchEditText)
EditText mSearchEditText; // errorRequired view 'searchEditText' with ID 2131558557 for field 'mSearchEditText' was not found
#BindView(R.id.storesRecyclerView)
RecyclerView recyclerView; // here too
private List<TestModel> stores;
private StoreAdapter mAdapter;
//endregion
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stores);
ButterKnife.bind(this);
loadRecyclerView();
testData();
}
}
activity_store layout
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="#+id/searchEditText"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
<android.support.v7.widget.RecyclerView
android:id="#+id/storesRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:scrollbars="vertical">
</android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
I understand that cause of error is that the ButterKnife.Bind in MainActivity is called when the Layout is inflated but the Views are not inflated in the inherited Activities . The Navigation drawer loads but on clicking item it fails as obvious reason. I cannot use a FrameLayout since I'm using MaterialDrawer library which doesn't ( or I'm not aware) have views to inflate.
I tried several methods extensively searching SO like SO1SO2 Git Git2
Link but was not successful .
Appreciate any help are deeply appreciated . Thanks in advance.
Thanks #mikepenz for the response.
Posting it since it might help somebody
The issue was fixed by creating an abstract method in BaseActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResourceId());
ButterKnife.bind(this);
setSupportActionBar(toolbar);
navManagement();
}
protected abstract int getLayoutResourceId();
Concrete Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_stores); not required
ButterKnife.bind(this);
loadRecyclerView();
testData();
}
#Override
protected int getLayoutResourceId() {
return R.layout.activity_stores;
}
Please note that I was not using ButterKnife.Bind in the Concrete Activity and used #Nullable fields. But currently Binding in both Base and Implementing Activities.
The MaterialDrawer will inflate views to your layout. By default it will add a DrawerLayout into the root of your activity, and add the other content (which is your main layout) as child to it again.
This decision was made to make it super simple to implement a Drawer into your application without needing to alter anything special.
Butterknife will bind the view's at the time you call the bind method.
All views of the MaterialDrawer, are bound at the time of you calling .build(), but as the views of the MaterialDrawer are bound internally you shouldn't have to worry about these.
To complete #user2695433 's answer, you can go a little futher : handle all butterknife init and close in the BaseActivity so you can't forget to release ressources ! Very useful if some new dev come in your team and dont know how butterknife work. Here it is :
--------In BaseActivity-------
protected Unbinder binder;
protected abstract int getLayoutResourceId();
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResourceId());
binder = ButterKnife.bind(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
binder.unbind(); //release ressource
}
----- In ConcreteActivity------------
#BindView(R.id.title)
TextView title; // butterknife stuff
#Override
protected int getLayoutResourceId() { //define which Id
return R.layout.activity_main;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

Android ViewPager not Showing

I am trying to make a little app to try out some things for a bigger app I am developing. I want to add a viewpager that displays four images, I developed this viewpager alone and it worked, but when I tried to add it to my other app (with just a couple of buttons) with the tag, it does not show anything and I am really stuck in here. Is possible to add the Viewpager this way? Honestly I don't have that much experience in Java, I usually program only C and Assembler and I will be reaaally glad if you could help me.
This is the xml file with the button and the include:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".MainActivity"
android:weightSum="1">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Calculo Rapido"
android:id="#+id/btcr"
android:layout_gravity="center_vertical"
android:layout_weight="0.16"
android:layout_alignParentStart="true" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Calculo Avanzado"
android:id="#+id/button"
android:layout_centerVertical="true"
android:layout_below="#+id/btcr" />
<include layout="#layout/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:translationZ="15dp" />
Now, the MainActivity, which holds the button activity because I kept the Viewpager inside an Activity that's linked with the viewpager original layout
Button siguiente;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
siguiente = (Button)findViewById(R.id.btcr);
siguiente.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent siguiente = new Intent(MainActivity.this, Main2Activity.class);
startActivity(siguiente);
}
});
}
And lastly, the code for the mainpager main activity, this uses a CustomPagerAdapter saved in another java class. As I told before, this viewpager does work alone, but when I try to implement it here, with the /include tag, it doesn't show anything.
public class MainActivity extends Activity {
CustomPagerAdapter mCustomPagerAdapter;
ViewPager mViewPager;
private static int currentPage = 0;
private static int NUM_PAGES = 0;
Button siguiente;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// == Setting up the ViewPager ==
mCustomPagerAdapter = new CustomPagerAdapter(this);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mCustomPagerAdapter);
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
if (currentPage == NUM_PAGES) {
currentPage = 0;
}
mViewPager.setCurrentItem(currentPage++);
if (currentPage == 5) {
currentPage = 0;
mViewPager.setCurrentItem(currentPage++, false);
}
}
};
Timer swipeTimer = new Timer();
swipeTimer.schedule(new TimerTask() {
#Override
public void run() {
handler.post(Update);
}
}, 2000, 2000);
}
Sorry if it is too long guys, but I am a little frustrated, I have tried a lot of things but it isn't working yet. Either the buttons do their thing and the ViewPager doesn't work or the ViewPager works but the buttons are dead.
I will reaaally aprecciate your help :)
create a new xml file inside layout folder and name it secondactivity.xml
and put this code
<?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.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/pager">
</android.support.v4.view.ViewPager>
</LinearLayout>
Now inside your Main2Activity.class
change
setContentView(R.layout.activity_main);
to
setContentView(R.layout.secondactivity);
And remove the include from your previous xml
Turns out, what I needed was just the simple solution. I had to put the viewpager on my activity's layout file as another object, just configuring its size and position. And then, on my onCreate I pasted the code of my functioning ViewPager. Quite simple, without requiring another layouts :)

Issues when relative layout implement swipe-to-refresh layout

I'm currently attempting to implement swipe-to-refresh layout into relative layout, but it is extremely insensitive and unstable. When I pull down the screen, it usually either doesn't refresh or it refreshes without progress bar.
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.miaor.tutorialweather.MainActivity"
android:id="#+id/refreshLayout"
android:background="#fe970b">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
</RelativeLayout>
public class MainActivity extends AppCompatActivity{
public static final String TAG = MainActivity.class.getSimpleName();
#BindView(R.id.TimeString) TextView mTimeLabel;
#BindView(R.id.TemperatureLabel) TextView mTemperatureLabel;
#BindView(R.id.HumidityValue) TextView mHumidityValue;
#BindView(R.id.rainingChanceValue) TextView mChance;
#BindView(R.id.weatherIcon) ImageView mWeatherIcon;
#BindView(R.id.SummaryText) TextView mSummaryText;
#BindView(R.id.refreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
///why do we make a new variable of CurrentWeather
private CurrentWeather mCurrentWeather;
private String apiKey = "6b9448b8e21c2abe2fb623b25554a77c";
private double latitude = 31.230708;
private double longitude = 121.472916;
private String forecastUrl = "https://api.forecast.io/forecast/" + apiKey +
"/" + latitude + "," + longitude;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
///implement swipe-to-refresh feature
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
Log.i(TAG, "onRefresh is working");
getForecast();
}
});
getForecast();
Log.d(TAG, "Main UI code is running!");
}
You can't implement SwipeToRefreshLayout with RelativeLayout as its child. You need to have a scrollable view as its only child. e.g. Listview, RecyclerView, ScrollView. That is the reason why it is not working as expected for you.
That is not the right position.
You need type like this:
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/your_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
YOUR RELATIVE LAYOUT HERE
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
Your code looks fine, it's weird that it's not working correctly... Maybe it's because of the ButterKnife library? I don't think that's the problem, but it will be worth the shot to get views manually to see if that's the problem
Then, if you need the loading animation to appear/hide you can use mSwipeRefreshLayout.setRefreshing(boolean);
For example in my app I attach the listener first (as you did in your example) and then I use the following code to show the loading animation while I fetch the data.
// Show loading while fetching the first set of data
mainSwipeRefreshLayout.post(new Runnable() {
#Override
public void run() {
mainSwipeRefreshLayout.setRefreshing(true);
}
});
In my fetchData() method I use the following line to hide the animation again when it finishes fetching the data.
// On load complete stop refresh animation
mainSwipeRefreshLayout.setRefreshing(false);
Hope this helps!
Andres.

OnClick doesn't work with LinearLayout

OnClick doesn't work. Nothing happens after clicking on layout. It seems like it is clickable, because layout changes its color, but new layout doesn't open.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/window"
android:layout_width="295dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#drawable/editborder"
android:clickable="true"
android:onClick="openBigImage">
Here is more code for Main Activity:
public class MyMapActivity extends FragmentActivity implements LocationListener
{
private Marker marker;
private Hashtable<String, String> markers;
private ImageLoader imageLoader;
private DisplayImageOptions options;
private GoogleMap map;
private ListView mainListView ;
private ArrayAdapter<String> listAdapter ;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_map);
// Look up the AdView as a resource and load a request.
//AdView adView = (AdView)this.findViewById(R.id.adView);
//adView.loadAd(new AdRequest());
// Getting Google Play availability status
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getBaseContext());
// Showing status
if(status!=ConnectionResult.SUCCESS)
{ // Google Play Services are not available
int requestCode = 10;
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(status, this, requestCode);
dialog.show();
}
else
{// Google Play Services are available
// Getting reference to the SupportMapFragment of activity_main.xml
SupportMapFragment mapFragment = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map);
if (savedInstanceState == null) {
// First incarnation of this activity.
mapFragment.setRetainInstance(true);
}
else
{
// Reincarnated activity. The obtained map is the same map instance in the previous
// activity life cycle. There is no need to reinitialize it.
map = mapFragment.getMap();
}
setUpMapIfNeeded();
}
}
#Override
protected void onResume()
{
super.onResume();
setUpMapIfNeeded();
}
public void openBigImage(View v)
{
setContentView(R.layout.bigpicture);
}
bigpicture.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/bigpicture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical">
<fragment
android:id="#+id/minimap"
android:layout_width="200px"
android:layout_height="200px"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
class="com.google.android.gms.maps.SupportMapFragment" />
<ImageView
android:id="#+id/badge"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:adjustViewBounds="true" />
</RelativeLayout>
Calling setContentView() multiple times worked in other cases, like menu items "about", "settings" etc.
Tried to make without setContentView. I've put new Layout to the main.xml and made visibility GONE. OnClick method should change visibility to visible, but again nothing happens.
Logcat says "11-25 13:47:28.638: D/GestureDetector(3156): [Surface Touch Event] mSweepDown False, mLRSDCnt : -1 mTouchCnt : 2 mFalseSizeCnt:0" when i'm clicking on linear layout.
Paul,
One thing is close the linear layout with /> .I am assuming that you have followed the map tutorials link and passed all the manifest permissions and other requirements. You might have some reasons to use px. Check if map is being created. Also give some height and background color to your badge image and see if something happens.
I tested your code without map fragment and it worked fine.
Can you post the error log ?
Found. It is a click on InfoWindow, so we should implement onInfoWindowClick.
But first we must add map.setOnInfoWindowClickListener(this); in main activity. Main activity must implement OnInfoWindowClickListener.
I've added new LinearLayout to the main.xml, made it invisible.
Here's code for onInfoWindowClick:
#Override
public void onInfoWindowClick(Marker marker) {
LinearLayout secondLL = (LinearLayout) findViewById(R.id.bigpicture);
int visibility = secondLL.getVisibility();
if(visibility == View.GONE)
{
secondLL.setVisibility(View.VISIBLE);
}
}
I think you can't use onClick attribute.
You have to use setOnClickListener() like that :
LinearLayout layout = (LinearLayout )findViewById(R.id.window);
layout .setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
YourActivity.this.setContentView(R.layout.bigpicture);
}
});

Customize the ListView in a Fragment-based Master-Detail Flow?

I found the template that ADT generates for a Master/Detail Flow Activity to be rather obtuse, and I don't like the fact that the ListView is not declared in a layout file which forces me to work with it programatically. For example, instead of just setting android:background on the ListView in a layout file, I'm forced to find the ListView via findViewById in the fragment's onCreateView(...) method, then call setBackground(...). For maintainability and general readability, I'd like to do the same thing via a layout file.
Instead, I'm trying to inflate a custom layout in the onCreateView method of the "master" fragment:
public class InboxFragment extends ListFragment {
public InboxFragment() {}
private ListView listView = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
listView = (ListView) inflater.inflate(R.layout.inbox_fragment_layout,
null);
return listView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// the following doesn't work
listView.setAdapter(new ArrayAdapter<DummyContent.DummyItem>(getActivity(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1,
DummyContent.ITEMS));
// nor does this
//setListAdapter(new ArrayAdapter<DummyContent.DummyItem>(getActivity(),
// android.R.layout.simple_list_item_activated_1,
// android.R.id.text1,
// DummyContent.ITEMS));
}
}
However, calling setListAdapter (or even setAdapter) doesn't seem to do anything, even though I've given the ListView an id of android:id="#id/android:list" in R.layout.inbox_fragment_layout:
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#D3D3D3" />
My "master" activity just calls a fragment layout:
public class InboxActivity extends FragmentActivity {
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.inbox_phone_layout);
}
}
inbox_phone_layout.xml:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentPhoneInbox"
android:name="com.justin.inbox.InboxFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
Instead, I'm greeted with a blank page and it doesn't look like the ListView is loaded at all. What am I doing wrong, here? Sample project on GitHub.
I fixed your issue by changing inbox_phone_layout.xml to the following:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentPhoneInbox"
android:name="com.justin.inbox.InboxFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />

Categories