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.
Related
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.
I have an XML layout file that has a TextView within a CoordinatorLayout.
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".SpecificProgramSelectionActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="75dp"
android:id="#+id/saved_program"
android:text="Empty"
android:textAlignment="center"
android:gravity="fill"
android:textSize="20dp"
android:background="#drawable/program_selection_border"
android:textColor="#color/black"
android:clickable="true"
android:focusable="true"
android:onClick="addToSavedPrograms"
android:paddingTop="5dp"
android:paddingBottom="5dp"/>
And this code that inflates the layout and adds it into a Linear Layout in the activity's view.
for (PlayerWithObjectives player : players){
name = player.getName();
for (String objective : player.getObjectives()){
objectives.add(objective);
}
nameView = inflater.inflate(R.layout.inflatable_player_name_view, null);
((TextView)nameView.findViewById(R.id.saved_program)).setText(name);
((TextView)nameView.findViewById(R.id.saved_program)).setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
((TextView)nameView.findViewById(R.id.saved_program)).setTextSize(20);
linearLayout.addView(nameView);
}
(This is the activity's layout XML)
<LinearLayout 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=".SpecificProgramSelectionActivity"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/specific_program_selection_linear_layout">
</LinearLayout>
</ScrollView>
Everything looks fine in the app when I run it. Every inflated view shows up, the only issue is that the method that I specified in the onClick attribute for the inflated TextView does not get called. Why is that? Here is the method that is supposed to be called
public void addToSavedPrograms(View view){
String name = (String) (((TextView)view.findViewById(R.id.saved_program)).getText());
namesToSend.add(name);
editor.putStringSet("Tracked Players", namesToSend);
editor.commit();
Toast.makeText(getApplicationContext(),name + " was saved to preferences.", Toast.LENGTH_SHORT);
}
Why doesn't the method get called? I already saw all the other threads about using setContent() and stuff but that didn't work and it was not explained that great in the answer. Any help would be greatly appreciated.
public class YourActivity extends AppCompatActivity implements OnClickListener{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textview=(TextView)findViewById(R.id.saved_program);
textview.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch(view.getId()){
case R.id.saved_program:
//call your function here
default:
break;
}
}
you can do it like this.
Update:
First off, thank you to everybody that commented, your help was greatly appreciated. Your no judgment assistance is an awesome and welcome change from a lot of what I have experienced and seen on in this community.
Secondly, I figured out my issue. Or got my code to work at least. Once I got a better understanding of inflating layouts I changed this line of code
nameView = inflater.inflate(R.layout.inflatable_player_name_view, null);
To
nameView = (TextView) inflater.inflate(R.layout.inflatable_add_player_name_view, linearLayout, false);
The difference is that When you inflate, it returns an object that is the same type as the top parent in the specified layout file (Which in this case is "R.layout.inflatable_player_name_view"). So I changed nameView to a TextView object and cast the returned object as a TextView. Then I specified the wanted parent layout (To get the right layoutParams) and put false so that it does not attach it automatically to that parent layout. After that I simply made the alterations to the textView that I wanted, like setting the text values and whatnot, and then manually added it to the parent linearlayout that I wanted. After doing this there was not even a need to set an onClickListener programmatically because the android:onClick method worked just fine.
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 :)
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);
}
});
I'm making a very simple Andriod app and I was wondering if I could get some help with my app.
I would like to show an ImageView over the full ListView (including the action bar) for 3 seconds, and then remove the ImageView (or hide it, anything), to go back to the list view.
How can this be done? I have tried a few things but it either breaks my code or doesn't show anything at all.
Thanks in advance everyone - let me know if you need any further explanation.
EDIT: As per the question below, I'd like the ImageView to be shown as soon as the ListView is shown, for 3 seconds, then disappear.
Allright. What you want is actually quite easy.
Simply create a RelativeLayout that contains a ListView and an ImageView above it.
Then inside your onCreate(...) method, you use a Handler and set the Visibility of the ImageView to GONE after 3 seconds.
Here is the layout.xml 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" >
<ListView
android:id="#+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
</ListView>
<ImageView
android:id="#+id/imageView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:scaleType="fitXY"
android:src="#drawable/your_image" />
</RelativeLayout>
And inside the onCreate(...) method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.yourlayout);
final ImageView iv = (ImageView) findViewById(R.id.imageView1);
Handler h = new Handler();
h.postDelayed(new Runnable() {
#Override
public void run() {
// EITHER HIDE IT IMMEDIATELY
iv.setVisibility(View.GONE);
// OR HIDE IT USING ANIMATION
hideImageAnimated(iv);
// DONT use both lines at the same time :)
}
}, 3000); // 3 seconds
}
In order to make things a bit smoother, you could use an AlphaAnimation on your ImageView:
public void hideImageAnimated(final ImageView iv) {
Animation alpha = new AlphaAnimation(1.0f, 0.0f);
alpha.setDuration(1000); // whatever duration you want
// add AnimationListener
alpha.setAnimationListener(new AnimationListener(){
#Override
public void onAnimationEnd(Animation arg0) {
iv.setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation arg0) { }
#Override
public void onAnimationStart(Animation arg0) { }
});
iv.startAnimation(alpha);
}