EDIT Happy New Year! To be precise ,I created a simple music app in Android Studio who read mp3 files from my internal storage. All good until I decide to put one random image on every single track on the list. (In front of the name of the song I want to appear one image).When I select one single image in 'src' at ImageView ,it works and appear to all songs. I will atach a part of my code below: Main Activity:
package com.alg.amzar.spectrum;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
private ArrayList<Song> songList;
private ListView songView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
schimbare();
songView = (ListView)findViewById(R.id.song_list);
songList = new ArrayList<Song>();
getSongList();
Collections.sort(songList, new Comparator<Song>(){
public int compare(Song a, Song b){
return a.getTitle().compareTo(b.getTitle());
}
});
SongAdapter songAdt = new SongAdapter(this, songList);
songView.setAdapter(songAdt);
}
public void schimbare() {
int[] photos={R.drawable.img_0, R.drawable.img_1,R.drawable.img_2};
ImageView image = (ImageView) findViewById(R.id.imagine);
Random ran=new Random();
int i=ran.nextInt(photos.length);
image.setImageResource(photos[i]);
}
public void getSongList() {
ContentResolver musicResolver = getContentResolver();
Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = musicResolver.query(musicUri, null, null, null, null);
if(musicCursor!=null && musicCursor.moveToFirst()){
//get columns
int titleColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.TITLE);
int idColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media._ID);
//add songs to list
do {
long thisId = musicCursor.getLong(idColumn);
String thisTitle = musicCursor.getString(titleColumn);
songList.add(new Song(thisId, thisTitle));
}
while (musicCursor.moveToNext());
}
}
public native String stringFromJNI();
}
Activity Main .xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#FF330000"
tools:context=".MainActivity" >
<ListView
android:id="#+id/song_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>
Song.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="fill_parent"
android:layout_height="wrap_content"
android:onClick="songPicked"
android:orientation="vertical"
android:padding="5dp" >
<ImageView
android:layout_width="70dp"
android:id="#+id/imagine"
android:layout_height="50dp" />
<TextView
android:id="#+id/song_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#FFFFFF99"
android:textSize="15sp"
android:textStyle="bold"
android:layout_marginBottom="20dp"
android:layout_marginLeft="75dp"
android:layout_marginTop="-42dp"/>
</LinearLayout>
Beside this ,I've 2 more java classes. I think the problem is here ,but idk what is wrong:
public void schimbare() {
int[] photos={R.drawable.img_0, R.drawable.img_1,R.drawable.img_2};
ImageView image = (ImageView) findViewById(R.id.imagine);
Random ran=new Random();
int i=ran.nextInt(photos.length);
image.setImageResource(photos[i]);
}
Another wondering of mine is what 'deprecated' means when is I use .getDrawable.
Thanks in advance!
Logcat:
ime: FATAL EXCEPTION: main
Process: com.alg.amzar.spectrum, PID: 28677
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.alg.amzar.spectrum/com.alg.amzar.spectrum.MainActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2381)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2433)
at android.app.ActivityThread.access$800(ActivityThread.java:155)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1346)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5341)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:830)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:646)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.alg.amzar.spectrum.MainActivity.schimbare(MainActivity.java:57)
at com.alg.amzar.spectrum.MainActivity.onCreate(MainActivity.java:31)
at android.app.Activity.performCreate(Activity.java:5343)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1088)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2335)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2433)
at android.app.ActivityThread.access$800(ActivityThread.java:155)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1346)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5341)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:830)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:646)
at dalvik.system.NativeStart.main(Native Method)
My phone android version: 4.4.2 (API 19)
Target SDK Version:"19"
MinSDK Version:"14"
SongAdapter.java:
package com.alg.amzar.spectrum;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
public class SongAdapter extends BaseAdapter {
private ArrayList<Song> songs;
private LayoutInflater songInf;
public SongAdapter(Context c, ArrayList<Song> theSongs) {
songs = theSongs;
songInf = LayoutInflater.from(c);
}
#Override
public int getCount() {
return songs.size();
}
#Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//map to song layout
LinearLayout songLay = (LinearLayout)songInf.inflate
(R.layout.song, parent, false);
//get title and artist views
TextView songView = (TextView)songLay.findViewById(R.id.song_title);
//get song using position
Song currSong = songs.get(position);
//get title and artist strings
songView.setText(currSong.getTitle());
//set position as tag
songLay.setTag(position);
return songLay;
}
}
In your case, findViewById(R.id.imagine) returns NULL because it doesn't exist in the layout 'activity_main', which you provided in setContentView(). findViewById() returns NULL if the view doesn't exist in current layout. R.id.imagine exists in the layout song.xml.
I think, you are trying to inflate the ListView with layout 'song'. Only after inflating you can call findViewById and setImageResource. You can done it in class SongAdapter, for each elements in ListView after inflating them.
Another suggestion, instead of using random images you can get album art of a song from MediaMetadataRetriever class. You can refer android documentation for that. But you have to solve this exception before.
Comment/ Delete function schimbare() in onCreate and edit SongAdapter like following.
package com.alg.amzar.spectrum;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
public class SongAdapter extends BaseAdapter {
private ArrayList<Song> songs;
private LayoutInflater songInf;
int[] photos={R.drawable.img_0, R.drawable.img_1,R.drawable.img_2};
Random ran=new Random();
// CHECK THIS:
public SongAdapter(Context c, ArrayList<Song> theSongs) {
songs = theSongs;
// CHECK THIS:
songInf = ( LayoutInflater )c.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return songs.size();
}
#Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//map to song layout
LinearLayout songLay = (LinearLayout)songInf.inflate
(R.layout.song, null);
//get title and artist views
TextView songView = (TextView)songLay.findViewById(R.id.song_title);
// CHECK THIS:
ImageView img = (ImageView)songLay.findViewById(R.id.imagine);
//get song using position
Song currSong = songs.get(position);
//get title and artist strings
songView.setText(currSong.getTitle());
// CHECK THIS:
int i=ran.nextInt(photos.length);
img.setImageResource(photos[i]);
//set position as tag
songLay.setTag(position);
return songLay;
}
}
Deprecated mean that google no longer support this method, they telling you there is NEWER method you should use. the deprecated method is working fine FOR NOW. But some day (maybe tomorrow maybe 3 years from today) it will no longer work.
about the error, upload the log please.
The error is at line 57:
at com.alg.amzar.spectrum.MainActivity.schimbare(MainActivity.java:57)
I am not sure where line 57 is but you can check it in your code, if you still cant find the cause then update us which line is 57
public void schimbare() {
int[] photos={R.drawable.img_0, R.drawable.img_1,R.drawable.img_2};
ImageView image = (ImageView) findViewById(R.id.imagine);
Random ran=new Random();
// CHANGE photos.length to photos.length-1
int i=ran.nextInt(photos.length);
//ADD this 2 line of code below, then you can check in your Log if the problem is with the view (the view is null), or if the photos position is null.
Log.d("bug_tag",String.valueOf(photos[i]));
Log.d("bug_tag",String.valueOf(image.getId()));
image.setImageResource(photos[i]);
}
Related
I am creating an app called Pricepointcrypto in which when a certain crypto currencies of your intersenst hit you budget range then you will get notfied say you wanted to buy Bitcoin 15,000 the stock market hits that you will get notfied, in this app i need to display crypto currencies and their rates and i am using coinmarket cap API for in adroid studio with java. i have already created the request and response functionalty in java and it does work on the backewnd but i have a layout issue on the rcyler view when it tries top render then when we want to setText it says its null
Here is the error i get:
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
I am just stuck on this part i have tried numerous tests so if someone could help it would be really helpfull
Thanks!
Here is the Code:
Main Acitivty.java
package com.example.pricepointcrypto;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
private ArrayList<CurrencyModal> currencyModalArrayList;
private CurrencyRVAdapter currencyRVAdapter;
private ProgressBar loadingPB;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText searchEdt = findViewById(R.id.idEdtCurrency);
// initializing all our variables and array list.
loadingPB = findViewById(R.id.idPBLoading);
// creating variable for recycler view,
// adapter, array list, progress bar
RecyclerView currencyRV = findViewById(R.id.idRVcurrency);
currencyModalArrayList = new ArrayList<>();
// initializing our adapter class.
currencyRVAdapter = new CurrencyRVAdapter(currencyModalArrayList, this);
// setting layout manager to recycler view.
currencyRV.setLayoutManager(new LinearLayoutManager(this));
// setting adapter to recycler view.
currencyRV.setAdapter(currencyRVAdapter);
// calling get data method to get data from API.
getData();
// on below line we are adding text watcher for our
// edit text to check the data entered in edittext.
searchEdt.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
// on below line calling a
// method to filter our array list
filter(s.toString());
}
});
}
private void filter(String filter) {
// on below line we are creating a new array list
// for storing our filtered data.
ArrayList<CurrencyModal> filteredlist = new ArrayList<>();
// running a for loop to search the data from our array list.
for (CurrencyModal item : currencyModalArrayList) {
// on below line we are getting the item which are
// filtered and adding it to filtered list.
if (item.getName().toLowerCase().contains(filter.toLowerCase())) {
filteredlist.add(item);
}
}
// on below line we are checking
// weather the list is empty or not.
if (filteredlist.isEmpty()) {
// if list is empty we are displaying a toast message.
Toast.makeText(this, "No currency found..", Toast.LENGTH_SHORT).show();
} else {
// on below line we are calling a filter
// list method to filter our list.
currencyRVAdapter.filterList(filteredlist);
}
}
private void getData() {
// creating a variable for storing our string.
String url = "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest";
// creating a variable for request queue.
RequestQueue queue = Volley.newRequestQueue(this);
// making a json object request to fetch data from API.
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null,
response -> {
// inside on response method extracting data
// from response and passing it to array list
// on below line we are making our progress
// bar visibility to gone.
loadingPB.setVisibility(View.GONE);
try {
// extracting data from json.
JSONArray dataArray = response.getJSONArray("data");
for (int i = 0; i < dataArray.length(); i++) {
JSONObject dataObj = dataArray.getJSONObject(i);
String symbol = dataObj.getString("symbol");
String name = dataObj.getString("name");
JSONObject quote = dataObj.getJSONObject("quote");
JSONObject USD = quote.getJSONObject("USD");
double price = USD.getDouble("price");
String finalName = ""+name+" - "+symbol+"";
// adding all data to our array list.
currencyModalArrayList.add(new CurrencyModal(finalName,
R.drawable.ic_launcher_foreground, price));
}
// notifying adapter on data change.
currencyRVAdapter.notifyDataSetChanged();
} catch (JSONException e) {
// handling json exception.
e.printStackTrace();
Toast.makeText(MainActivity.this, "Something went amiss. Please try again later",
Toast.LENGTH_SHORT).show();
}
}, error -> {
// displaying error response when received any error.
Toast.makeText(MainActivity.this, "Something went amiss. Please try again later",
Toast.LENGTH_SHORT).show();
}) {
#Override
public Map<String, String> getHeaders() {
// in this method passing headers as
// key along with value as API keys.
HashMap<String, String> headers = new HashMap<>();
headers.put("X-CMC_PRO_API_KEY", "106d7841-b282-4c8c-aa0f-d53509398468");
// at last returning headers
return headers;
}
};
// calling a method to add our
// json object request to our queue.
queue.add(jsonObjectRequest);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:background="#color/blac_shade_1"
tools:context=".MainActivity">
<!--edit text for searching our currency-->
<EditText
android:id="#+id/idEdtCurrency"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:focusable="auto"
android:hint="Search Currency"
android:textColor="#color/white"
android:textColorHint="#color/white" />
<!--recycler view for displaying the list of currencies-->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/idRVcurrency"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/idEdtCurrency"
tools:listitem="#layout/crypto_card_layout" />
<!--progress bar for loading indicator-->
<ProgressBar
android:id="#+id/idPBLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone" />
</RelativeLayout>
CurrencyRVApdapter
package com.example.pricepointcrypto;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.text.DecimalFormat;
import java.util.ArrayList;
// on below line we are creating our adapter class
// in this class we are passing our array list
// and our View Holder class which we have created.
public class CurrencyRVAdapter extends
RecyclerView.Adapter<CurrencyRVAdapter.CurrencyViewholder> {
private static DecimalFormat df2 = new DecimalFormat("#.##");
private ArrayList<CurrencyModal> currencyModals;
private Context context;
public CurrencyRVAdapter(ArrayList<CurrencyModal> currencyModals, Context context) {
this.currencyModals = currencyModals;
this.context = context;
}
// below is the method to filter our list.
public void filterList(ArrayList<CurrencyModal> filterllist) {
// adding filtered list to our
// array list and notifying data set changed
currencyModals = filterllist;
notifyDataSetChanged();
}
#NonNull
#Override
public CurrencyRVAdapter.CurrencyViewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
// this method is use to inflate the layout file
// which we have created for our recycler view.
// on below line we are inflating our layout file.
View view = LayoutInflater.from(context).inflate(R.layout.crypto_card_layout , parent, false);
return new CurrencyViewholder(view);
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(#NonNull CurrencyRVAdapter.CurrencyViewholder holder, int position) {
// on below line we are setting data to our item of
// recycler view and all its views.
CurrencyModal modal = currencyModals.get(position);
holder.nameTV.setText(modal.getName());
holder.rateTV.setText("$ " + df2.format(modal.getPrice()) + " USD");
holder.symbolTV.setImageResource(modal.getSymbol());
}
#Override
public int getItemCount() {
// on below line we are returning
// the size of our array list.
return currencyModals.size();
}
// on below line we are creating our view holder class
// which will be used to initialize each view of our layout file.
public class CurrencyViewholder extends RecyclerView.ViewHolder {
private ImageView symbolTV;
private TextView rateTV, nameTV;
public CurrencyViewholder(#NonNull View itemView) {
super(itemView);
// on below line we are initializing all
// our text views along with its ids.
symbolTV = itemView.findViewById(R.id.currency_logo);
rateTV = itemView.findViewById(R.id.currency_Rate);
nameTV = itemView.findViewById(R.id.currency_name);
}
}
}
CurrencyModal
package com.example.pricepointcrypto;
public class CurrencyModal {
// variable for currency name,
// currency symbol and price.
private String name;
private int symbol;
private double price;
public CurrencyModal(String name, int symbol, double price) {
this.name = name;
this.symbol = symbol;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSymbol() {
return symbol;
}
public void setSymbol(int symbol) {
this.symbol = symbol;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
crypto_card_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
>
<androidx.cardview.widget.CardView
android:id="#+id/currency_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardBackgroundColor="#03A9F4"
card_view:cardCornerRadius="20dp"
card_view:cardElevation="5dp"
card_view:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/currency_logo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:src="#drawable/ic_launcher_foreground"
android:tag="image_tag"
android:contentDescription="#string/currencylogo" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:id="#+id/currency_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_horizontal"
android:layout_marginTop="10dp"
android:fontFamily="casual"
android:text="Currency Name"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="#FFEB3B"
android:textStyle="bold" />
<TextView
android:id="#+id/currency_Rate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_horizontal"
android:layout_marginTop="0dp"
android:fontFamily="#font/stick"
android:text="Current Rate: "
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#FFFFFF"
android:textColorHighlight="#FFFFFF"
android:textStyle="bold|italic" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
View view = LayoutInflater.from(context).inflate(R.layout.crypto_card_layout , parent, false);
Try setting LayoutInflater.from(parent.getContext()) instead of LayoutInflater.from(context)
I have a fragment which is to display a lyrics of a song which is loaded from room databse based on its given id.
I'd like to preserve scrolling position after rotating the screen. Now after rotating the song is loaded again from db and the view is on the very top regardless of the scrolling position befor the rotating.
I thought that I can save scrolling position in onSaveInstanceState some bundle in onCreateView() use command on mSongDisplayScrollView.scrollTo(x, y)
The fragment code:
public class SongDisplayFragment extends Fragment {
private Song mSongToDisplay;
private ScrollView mSongDisplayScrollView;
private TextView mSongTitleTextView;
private RecyclerView mSongLyricsRecyclerView;
private GuitarSongbookViewModel mGuitarSongbookViewModel;
public static final String SONG_ID_KEY = "SONG_ID_KEY";
public SongDisplayFragment() {
}
public static SongDisplayFragment newInstance(Long songId) {
SongDisplayFragment songDisplayFragment = new SongDisplayFragment();
Bundle arguments = new Bundle();
arguments.putLong(SONG_ID_KEY,
songDisplayFragment.setArguments(arguments);
return songDisplayFragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(false);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_song_display, container, false);
mSongDisplayScrollView = view.findViewById(R.id.song_display_scroll_view);
mSongLyricsRecyclerView = view.findViewById(R.id.lyrics_rv_);
mSongTitleTextView = view.findViewById(R.id.
mGuitarSongbookViewModel = ViewModelProviders.of(this).get(GuitarSongbookViewModel.class);
final SongDisplayAdapter songDisplayAdapter = new SongDisplayAdapter(getContext());
Long songId = null;
if (getArguments().containsKey(SONG_ID_KEY)) {
songId = getArguments().getLong(SONG_ID_KEY);
}
if (songId != null) {
final Long finalSongId = songId;
mGuitarSongbookViewModel.getSongById(songId).observe(this, new Observer<Song>() {
#Override
public void onChanged(#Nullable final Song song) {
mSongToDisplay = song;
mSongTitleTextView.setText(mSongToDisplay.getMTitle());
songDisplayAdapter.setSong(song);
}
});
}
mSongLyricsRecyclerView.setAdapter(songDisplayAdapter);
mSongLyricsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
return view;
}
}
The fragment XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.SongDisplayFragment">
<ScrollView
android:id="#+id/song_display_scroll_view"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/autoscroll_bar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0"
tools:layout_editor_absoluteX="138dp">
<LinearLayout
android:id="#+id/son_display_linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/displayed_song_title_txt_"
style="#style/TitleOfDisplayedSong"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:text="#string/title_placeholder" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/lyrics_rv_"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="12dp"
android:nestedScrollingEnabled="false" />
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
The fragment displays a tiltle and lyrics of a song in a textView and RecyclerView which adapter class code is:
package com.example.guitarsongbook.adapters;
import android.content.Context;
import android.graphics.Rect;
import android.text.Html;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.guitarsongbook.R;
import com.example.guitarsongbook.daos.SongChordJoinDao;
import com.example.guitarsongbook.model.Chord;
import com.example.guitarsongbook.model.Song;
import java.util.ArrayList;
import java.util.List;
public class SongDisplayAdapter extends RecyclerView.Adapter<SongDisplayAdapter.SongViewHolder> {
private Context context;
private final LayoutInflater mInflater;
private Song mSong;
private ArrayList<String> mLyrics;
public SongDisplayAdapter(Context context) {
this.context = context;
mInflater = LayoutInflater.from(context);
}
#NonNull
#Override
public SongViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = mInflater.inflate(R.layout.song_lyrics_rv_item, parent, false);
return new SongDisplayAdapter.SongViewHolder(itemView);
}
public void setSong(Song song){
mSong = song;
mLyrics = mSong.getMLyrics();
notifyDataSetChanged();
}
#Override
public void onBindViewHolder(#NonNull SongViewHolder holder, int position) {
holder.bindTo(position);
}
#Override
public int getItemCount() {
if (mLyrics != null)
return mLyrics.size();
else return 0;
}
public class SongViewHolder extends RecyclerView.ViewHolder {
private final TextView mLyricsLineTextView;
public SongViewHolder(#NonNull View itemView) {
super(itemView);
mLyricsLineTextView = itemView.findViewById(R.id.song_lyric_line_txt_);
}
public void bindTo(int position) {
if (mSong != null) {
mLyricsLineTextView.setText(Html.fromHtml(mLyrics.get(position)));
} else {
mLyricsLineTextView.setText(context.getString(R.string.no_song_label));
}
}
}
}
RecyclerView item XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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="wrap_content">
<TextView
android:id="#+id/song_lyric_line_txt_"
style="#style/LyricOfDisplayedSong"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="24dp"
app:layout_constraintEnd_toStartOf="#+id/song_chord_line_txt_"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="placeholder text" />
</androidx.constraintlayout.widget.ConstraintLayout>
I think that the problem is that after rotating the screen song has to be loaded again by observer's methon onChange() so there is a short moment that there is nothing to display for scrollView. I'd like to find some solution how to return to the old position after again loading the song.
The first step is to persist the values of the objects through your Fragment's onSaveInstanceState method.
More of my explanations would come from the comments within each code block, so you can understand better on how it all works.
Values to be persisted: mSongToDisplay and mScrollViewPosition
//new variable introduced
int mScrollViewPosition = 0;
#Override
protected void onSaveInstanceState(Bundle outState) {
//If screen orientation changes, android redraws all views
//and non persistent data would be lost.
//Nevertheless, before this happens android informs the app via this method
//So to prevent state loss we save the data to a temporary storage
outState.putParcelable("song_data", mSongToDisplay);
//save ScrollView current position
outState.putInt("position_data", mSongDisplayScrollView.getScrollY());
//call super to commit your changes
super.onSaveInstanceState(outState);
}
Next is to recover the data after orientation change is completed
Add the following block in your fragments onCreateView and make sure the if statement block comes after initializing your adapter
...
//...add adapter initialization block here before checking for saved data
//Check for saved data
if (savedInstanceState != null) {
// Retrieve the data you saved
mSongToDisplay = savedInstanceState.getParcelable("song_data");
mScrollViewPosition = savedInstanceState.getInt("position_data");
//Re-initialize and reload adapter record
mSongToDisplay = song;
mSongTitleTextView.setText(mSongToDisplay.getMTitle());
songDisplayAdapter.setSong(song);
}
else {
//No data to retrieve
//initialize data model here.
}
//Assign the adapter to recyclerView
mSongLyricsRecyclerView.setAdapter(songDisplayAdapter);
mSongLyricsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
try{
//Finally we set the scrollview position at this point just to make sure
//recyclerview has its data loaded and full length of scrollview is restored
mSongDisplayScrollView.setScrollY(mScrollViewPosition);}
catch(Exception ex){
//position isn't right
}
return view;
At this point the required data has been persisted and on every screenOrientationChange the app would load the previously saved song object into the adapter preventing a false call to the db or api, and would also scroll to the expected position using the saved integer value mScrollViewPosition.
I'm new to Android and have been learning about Android app development for about a month just through a book, and right now, I'm working on my own project, which is a basic app that manages user tasks and schedule. There is this one problem I am having. I am trying to add a new task to a RecyclerView list that contains the list of main tasks, but I keep getting errors. Here is what I have (I don't want to show everything because it is a project):
The RecyclerView list (main_task_window.xml):
<?xml version="1.0" encoding="utf-8"?>
<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">
<android.support.v7.widget.RecyclerView
android:id="#+id/MainTaskList"
android:layout_width="match_parent"
android:layout_height="449dp"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:layout_marginStart="0dp"
android:layout_marginTop="1dp"
android:layout_marginBottom="61dp" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/AddaMainTask"
android:onClick="addnewtask"
android:layout_width="54dp"
android:layout_height="51dp"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginEnd="13dp"
android:layout_marginBottom="10dp"
android:clickable="true"
app:srcCompat="#android:drawable/ic_input_add" />
</RelativeLayout>
The window that allows users to add new main tasks as well as additional notes (add_new_main_task_window.xml):
<?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">
<EditText
android:id="#+id/maintasks"
android:layout_width="match_parent"
android:layout_height="62dp"
android:hint="Main Task"/>
<EditText
android:id="#+id/notes"
android:layout_width="match_parent"
android:layout_height="62dp"
android:hint="Additional notes"/>
<Button
android:id="#+id/addButton"
android:onClick="sendButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add to main task" />
</LinearLayout>
Now here are the following Java files:
1. MainTasks.java (contains the get and set methods for the main task and additional notes)
package com.ricstudios.daymanager;
public class MainTasks
{
private String mt, an; //'mt' stands for "main tasks". 'an' stands for "additional notes"
//MainTasks class constructor
public MainTasks(String mt, String an)
{
//mt and an in the parameters is equal to the mt and an variables above
this.mt = mt;
this.an = an;
}
public String getMainTasks() //this get method obtains the main task string input)
{
return mt;
}
public void setMainTasks(String MainTasks) //this set methods stores the main task string input)
{
this.mt = MainTasks;
}
public String getAdditionalNotes() //this get method obtains the main task string input
{
return an;
}
public void setAdditionalNotes(String AddNotes) //this set method stores the main task string
{
this.an = AddNotes;
}
}
MainTaskAdapter.java (contains the adapter to render the data)
package com.ricstudios.daymanager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import java.util.List;
public class MainTaskAdapter extends RecyclerView.Adapter<MainTaskAdapter.ViewHolder>
{
private List<MainTasks> maintasklist; //will contain the main task and additional notes string inputs
public class ViewHolder extends RecyclerView.ViewHolder
{
public EditText maintask, addnotes;
//provides a reference to the views for each data item
public ViewHolder(View view)
{
super(view);
maintask = (EditText)view.findViewById(R.id.maintasks);
addnotes = (EditText)view.findViewById(R.id.notes);
}
}
//MainTaskAdapter class constructor
public MainTaskAdapter(List<MainTasks> maintasklist)
{
this.maintasklist = maintasklist;
}
//create new view (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.add_new_main_task_window, parent, false);
return new ViewHolder(itemView);
}
//replaces the contents of the main task view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position)
{
MainTasks obj = maintasklist.get(position); //MainTasks class obj called 'obj', which allows access to the MainTasks class
//obtains the main task and additional notes from the MainTask class
holder.maintask.setText(obj.getMainTasks());
holder.addnotes.setText(obj.getAdditionalNotes());
}
//returns the size of the main task list (invoked by the layout manager)
#Override
public int getItemCount()
{
return maintasklist.size();
}
}
AddNewMainTask.java (adds the tasks and additional notes to the RecyclerView)
package com.ricstudios.daymanager;
import android.support.v7.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.Button;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/*This class handles the window for adding new main tasks to the main task list (this window is the add_new_main_task_window.xml layout)*/
public class AddNewMainTask extends AppCompatActivity
{
private Button addbutton; //Button element from the add_new_main_task_window.xml layout
private EditText maintask, addnotes; //EditText elements from the add_new_main_task_window.xml layout
private List<MainTasks> maintasklist = new ArrayList<>();
private RecyclerView ListofMainTasks; //RecyclerView list element from the add_new_main_task_window.xml layout (containing the Main Task list)
private MainTaskAdapter TAdapter; //MainTaskAdapter class object 'TAdapter', allows access to the MainTaskAdapter class
/*sendButton method from the Button element in the add_new_main_task_window.xml and adds the main task and additional notes to the RecyclerView main task list*/
public void sendButton(View view)
{
maintask = (EditText)view.findViewById(R.id.maintasks);
addnotes = (EditText)view.findViewById(R.id.notes);
ListofMainTasks = (RecyclerView)findViewById(R.id.MainTaskList);
TAdapter = new MainTaskAdapter(maintasklist);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
ListofMainTasks.setLayoutManager(mLayoutManager);
ListofMainTasks.setItemAnimator(new DefaultItemAnimator());
ListofMainTasks.setAdapter(TAdapter);
/*Passes the EditText element values into the MainTask.java parameters*/
MainTasks mnt = new MainTasks(maintask.getText().toString(), addnotes.getText().toString());
maintasklist.add(mnt);
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.add_new_main_task_window); //displays the window that allows users to add new main tasks (which is the add_new_main_task_window.xml layout)
addbutton = (Button)findViewById(R.id.addButton);
/*When user clicks button, the values the user puts in the EditText fields will be added to the RecyclerView list of the main_task_window.xml layout*/
addbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v)
{
sendButton(v);
}
});
}
}
Whenever I press the "Add to Main Task" button, the app suddenly stop. Please help. Oh, and about the floatingactionbutton in the main_task_window.xml, don't worry about that. That just sends the user to the add_new_main_task_window.xml
Update: 3/5/19
I'm getting this error from the logcat:
03-05 15:22:49.298 10335-10335/com.project.daymanager E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.project.daymanager, PID: 10335
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.RecyclerView.setLayoutManager(android.support.v7.widget.RecyclerView$LayoutManager)' on a null object reference
at com.project.daymanager.AddNewMainTask.sendButton(AddNewMainTask.java:39)
at com.project.daymanager.AddNewMainTask$1.onClick(AddNewMainTask.java:63)
at android.view.View.performClick(View.java:5702)
at android.widget.TextView.performClick(TextView.java:10887)
at android.view.View$PerformClick.run(View.java:22541)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7229)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Replace
view.findViewById();
with
findViewById();
In the MainTasks.java Don't use a constructor
public MainTasks(String mt, String an)
Just keep the set And get methods and do
MainTasks mt = new MainTasks();
mt.setMainTasks("Title");
mt.setAdditionalNotes("Note");
list.add(mt);
If it still not working, please post the error log so we can know whats the error.
Update: 3/5/19
You are trying to access the recyclerView of the MainTaskActivity in
ListofMainTasks = (RecyclerView)findViewById(R.id.MainTaskList);
from out side the activity (From another Activity) and that's not possible this way,
you can set the RecyclerView in the MainTaskActivity to static and use it in AddNewMainTask Activity by calling it
MainTaskActivity.myRecyclerView.
Problem is in this line:
ListofMainTasks = (RecyclerView)findViewById(R.id.MainTaskList);
program is unable to find recycler view and its null. and you can't set layout manager to a null object. I've reviewed your code and thing you did wrong is that in you onCreate method you are setting layout setContentView(R.layout.add_new_main_task_window); and you're trying to find recylerview which is not in this layout.
Your recycler view is in main_task_window.xml. use setContentView(R.layout.main_task_window.xml); and your activity will find recyclerview easily.
Note: if you want button/controls also which are in this layout add_new_main_task_window.xml then please move them in layout with recyclerview.
I tested my app setting the device in airplane mode, and it crashed.
MoviesListFragment.java
package com.example.android.popularmoviesstage_1;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* A Class that extends Fragment to implement the Movie List structure
* A Fragment represents a behavior or a portion of user interface in a FragmentActivity.
*/
public class MoviesListFragment extends Fragment{
public static ProgressBar mLoadingIndicator;
public static TextView mErrorMessageDisplay;
public static SwipeRefreshLayout mSwipeContainer;
public static PosterAdapter mMoviesAdapter;
private Context mContext;
private MoviesRecyclerView mScrollListener;
private int mPage;
private int mSorting;
private static final int SORTING_POPULAR = 1;
private static final int SORTING_RATED = 2;
private static final String BUNDLE_MOVIES_KEY = "movieList";
private static final String BUNDLE_PAGE_KEY = "currentPage";
private static final String BUNDLE_SORTING_KEY = "currentSorting";
private static final String BUNDLE_ERROR_KEY = "errorShown";
private static final String TAG = MoviesListFragment.class.getSimpleName();
#Override
public void onCreate(#Nullable Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true); //Allowing menu options in the ActionBar
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState){
Boolean errorShown = false;
if (savedInstanceState != null){
errorShown = savedInstanceState.getBoolean(BUNDLE_ERROR_KEY);
}
if (savedInstanceState != null && !errorShown){
mPage = savedInstanceState.getInt(BUNDLE_PAGE_KEY);
mSorting = savedInstanceState.getInt(BUNDLE_SORTING_KEY);
} else {
mPage = 1;
mSorting = 1;
}
//inflating the movies in this fragment
View rootView = inflater.inflate(R.layout.movie_list_fragment, container, false);
mContext = getContext();
final int columns = getResources().getInteger(R.integer.grid_columns);
// Laying the movie items in grid formation.
GridLayoutManager gridLayoutManager = new GridLayoutManager(mContext, columns, GridLayoutManager.VERTICAL, false);
RecyclerView recyclerView = rootView.findViewById(R.id.rv_posters);
recyclerView.setLayoutManager(gridLayoutManager);
//setting the size for all movie items
recyclerView.setHasFixedSize(true);
mMoviesAdapter = new PosterAdapter();
recyclerView.setAdapter(mMoviesAdapter);
//progress indicator catching movie data from the internet
mLoadingIndicator = rootView.findViewById(R.id.pb_loading_indicator);
mScrollListener = new MoviesRecyclerView(gridLayoutManager, mPage) {
#Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
Log.d(TAG, "Loading page: " + String.valueOf(page));
mPage = page;
loadCards(mSorting);
}
};
recyclerView.addOnScrollListener(mScrollListener);
//The SwipeRefreshLayout is used whenever the user refresh the contents of a view via a vertical swipe gesture.
mSwipeContainer = rootView.findViewById(R.id.sr_swipe_container);
mSwipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
mErrorMessageDisplay.setVisibility(View.INVISIBLE);
clearGridView();
loadCards(mSorting);
}
});
mSwipeContainer.setColorSchemeResources(R.color.colorAccent);
mErrorMessageDisplay = rootView.findViewById(R.id.tv_error_message_display);
if (savedInstanceState != null && !errorShown){
ArrayList<Movie> movieArrayList = savedInstanceState.getParcelableArrayList(BUNDLE_MOVIES_KEY);
mMoviesAdapter.setMoviesData(movieArrayList);
} else {
loadCards(mSorting);
}
return rootView;
}
//onSaveInstanceState() is called before your activity is paused.
// So any info that it needs after it is potentially destroyed can be retrieved from the saved Bundle
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
List<Movie> movieList = mMoviesAdapter.getMoviesData();
if (movieList != null){
ArrayList<Movie> movieArrayList = new ArrayList<>(mMoviesAdapter.getMoviesData());
outState.putParcelableArrayList(BUNDLE_MOVIES_KEY, movieArrayList);
outState.putInt(BUNDLE_PAGE_KEY, mPage);
outState.putInt(BUNDLE_SORTING_KEY, mSorting);
} else {
if (mErrorMessageDisplay.isShown()){
outState.putBoolean(BUNDLE_ERROR_KEY, true);
}
}
}
/**
* A method that invokes the AsyncTask to populate the RecyclerView,
* it's based on the sorting option selected by the user. Default is "popular movies"
*
* #param sorting the way of sorting selected by the user
*/
private void loadCards(int sorting){
if(NetworkUtils.isOnline(mContext)){
String method;
switch (sorting){
case SORTING_POPULAR:
method = NetworkUtils.getMoviesPopular();
break;
case SORTING_RATED:
method = NetworkUtils.getMoviesTopRated();
break;
default:
method = NetworkUtils.getMoviesPopular();
break;
}
String[] posters = new String[]{method, String.valueOf(mPage)};
new FetchMovieTask().execute(posters);
} else {
showErrorMessage(R.string.error_no_connectivity);
if (mSwipeContainer.isRefreshing()) {
mSwipeContainer.setRefreshing(false);
}
}
}
/**
* Reset the GridView properties and adapter
*/
private void clearGridView(){
mScrollListener.resetState();
mPage = 1;
mMoviesAdapter.clear();
}
/**
* Display the specific error message in the TextView
*
* #param messageId the resource id of the error string
*/
public static void showErrorMessage(int messageId){
mErrorMessageDisplay.setText(Resources.getSystem().getText(messageId));
mErrorMessageDisplay.setVisibility(View.VISIBLE);
}
//onCreateOptionsMenu() to specify the options menu for an activity.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.main, menu);
switch (mSorting) {
case SORTING_POPULAR:
menu.findItem(R.id.sort_popular).setChecked(true);
break;
case SORTING_RATED:
menu.findItem(R.id.sort_rated).setChecked(true);
break;
default:
menu.findItem(R.id.sort_popular).setChecked(true);
break;
}
}
/*
*When the user selects an item from the options menu (including action items in the app bar),
* the system calls our activity's onOptionsItemSelected() method.
* This method passes the MenuItem selected. We can identify the item by calling getItemId(),
* which returns the unique ID for the menu item (defined by the android:id attribute in the menu
* resource or with an integer given to the add() method). We can match this ID against known menu
* items to perform the appropriate action.
*/
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.sort_popular || id == R.id.sort_rated) {
if (!item.isChecked()) {
mSorting = item.getOrder();
item.setChecked(true);
clearGridView();
loadCards(mSorting);
}
return true;
}
return super.onOptionsItemSelected(item);
}
}
MainActivity.java
package com.example.android.popularmoviesstage_1;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
/**
* MainActivity, which is presented to the user when the app is launched.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//MoviesListFragment is used here
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.popularmoviesstage_1.MainActivity">
<!-- This fragment is used in MainActivity -->
<fragment
android:name="com.example.android.popularmoviesstage_1.MoviesListFragment"
android:id="#+id/f_movie_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="#layout/movie_list_fragment" />
</FrameLayout>
movie_list_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/padding_ten"
android:paddingRight="#dimen/padding_ten">
<TextView
android:id="#+id/tv_error_message_display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="#dimen/padding_sixteen"
android:text="#string/error_message"
android:drawableRight="#android:drawable/stat_notify_error"
android:drawableEnd="#android:drawable/stat_notify_error"
android:drawableTint="#android:color/holo_red_dark"
android:drawablePadding="#dimen/padding_four"
android:textSize="#dimen/movie_detail_text_size"
android:background="#android:color/white"
android:visibility="invisible" />
<ProgressBar
android:id="#+id/pb_loading_indicator"
android:layout_width="#dimen/padding_fourty_two"
android:layout_height="#dimen/padding_fourty_two"
android:layout_gravity="center"
android:indeterminateTint="#android:color/holo_blue_bright"
android:visibility="invisible" />
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sr_swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_posters"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
strings.xml
<resources>
<string name="app_name">Popular Movies</string>
<string name="sort_popular">Most Popular</string>
<string name="sort_rated">Top Rated</string>
<string name="poster_image_alt">Poster Image</string>
<string name="error_message">An error has occurred. Please try again by swiping down</string>
<string name="error_no_connectivity">Your device is not connected to the Internet.</string>
<string name="error_moviedb_list">Error from the MovieDB Service. Swipe down to try again</string>
<string name="error_movie_poster">Error loading the poster, sorry!</string>
<string name="no_internet_connection">No Internet connection</string>
<string name="button_retry">RETRY</string>
<string name="checkInternetConnection">Check Internet</string>
</resources>
This is the error description:
FATAL EXCEPTION: main
Process: com.example.android.popularmoviesstage_1, PID: 21294
android.content.res.Resources$NotFoundException: String resource ID #0x7f0d0026
Here is the stacktrace of this error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.popularmoviesstage_1, PID: 21294
android.content.res.Resources$NotFoundException: String resource ID #0x7f0d0026
at android.content.res.Resources.getText(Resources.java:331)
at com.example.android.popularmoviesstage_1.MoviesListFragment.showErrorMessage(MoviesListFragment.java:185)
at com.example.android.popularmoviesstage_1.MoviesListFragment.loadCards(MoviesListFragment.java:161)
at com.example.android.popularmoviesstage_1.MoviesListFragment.access$300(MoviesListFragment.java:28)
at com.example.android.popularmoviesstage_1.MoviesListFragment$2.onRefresh(MoviesListFragment.java:104)
at android.support.v4.widget.SwipeRefreshLayout$1.onAnimationEnd(SwipeRefreshLayout.java:188)
at android.support.v4.widget.CircleImageView.onAnimationEnd(CircleImageView.java:106)
at android.view.ViewGroup.finishAnimatingView(ViewGroup.java:6278)
at android.view.View.draw(View.java:17027)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.draw(View.java:17086)
at android.view.View.updateDisplayListIfDirty(View.java:16065)
at android.view.View.draw(View.java:16849)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.updateDisplayListIfDirty(View.java:16060)
at android.view.View.draw(View.java:16849)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.updateDisplayListIfDirty(View.java:16060)
at android.view.View.draw(View.java:16849)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.updateDisplayListIfDirty(View.java:16060)
at android.view.View.draw(View.java:16849)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.updateDisplayListIfDirty(View.java:16060)
at android.view.View.draw(View.java:16849)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.draw(View.java:17086)
at com.android.internal.policy.DecorView.draw(DecorView.java:751)
at android.view.View.updateDisplayListIfDirty(View.java:16065)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:657)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:663)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:771)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2808)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2616)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2223)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1258)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6348)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:871)
at android.view.Choreographer.doCallbacks(Choreographer.java:683)
at android.view.Choreographer.doFrame(Choreographer.java:619)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:857)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
In order to get a String in a fragment use the following code
getResources().getString(R.id.error_no_connectivity).
This will fix the crash for you. You are not referencing the string in your strings.xml correctly
You method should be (Removed the static keyword)
public void showErrorMessage(int messageId){
mErrorMessageDisplay.setText(getResources().getString(messageId));
mErrorMessageDisplay.setVisibility(View.VISIBLE);
}
I need your help on this. I am going to make an app using ViewPager and I since I never been programming for android before I thought it would be good to make a sample app first. I want to use the ViewPager a little bit differently than the classic list-of-items-style so I made an app that will show all the colors (or every 10th color) from #000000 to #FFFFFF.
It doesn't work. I've started the app on the emulator but I just get a white screen. If the default position of the ViewPager when starting is 0 then the color should be black. And when I try to make a breakpoint the program doesn't stop, or it's never reaching the point. I'm using eclipse.
These are the files of the project
MainActivity.java
package com.example.colorswipe;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
public class MainActivity extends Activity {
private ViewPager mPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPager=(ViewPager)this.findViewById(R.id.pager);
mPager.setAdapter(new MyAdapter());
}
private class MyAdapter extends PagerAdapter {
#Override
public int getCount() {
return 0xFFFFFF/10;
}
#Override
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return false;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
ColorView view=new ColorView(container.getContext());
view.setBackgroundColor(android.graphics.Color.parseColor(String.format("#%06X", position*10)));
view.setText(position);
container.addView(view);
return view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
}
}
activity_main.xml
<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.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
ColorView.java
package com.example.colorswipe;
import android.content.Context;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ColorView extends LinearLayout {
private TextView tv;
public ColorView(Context context) {
super(context);
LayoutParams params=new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
this.setLayoutParams(params);
TextView tv=new TextView(context);
this.tv=tv;
this.addView(tv);
}
public void setText(int position) {
tv.setText(Integer.toString(position).toCharArray(), 0, Integer.toString(position).length());
}
}
EDIT - the view pager is there , but I think you don't resolve the color correctly.
I found the problem. isViewFromObject must be implemented as return arg0==arg1.