So my problem is that I can generate the data in the AsyncTask, but I can't get the data to transfer back to the onCreateView. This is my first dive into fragments. Sorry if my code's ugly, I'm just trying to get this to work, and also I'm quite new to Android programming, so sorry if I made a stupid mistake somewhere. I'm not sure if I need to post my global variables. And I know some of my variables are weirdly named.
onCreateView portion
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_news,
container, false);
pb = (ProgressBar) getActivity().findViewById(R.id.progressBar2);
gv = (RecyclerView) view.findViewById(R.id.expandableListView);
swp = (SwipeRefreshLayout) getActivity().findViewById(R.id.activity_main_swipe_refresh_layout);
gv.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
gv.setLayoutManager(layoutManager);
swp.setEnabled(false);
hadapter = new NewsExpandableAdapter(getActivity(), m_hparts);
System.out.println(m_hparts.size());
gv.setAdapter(hadapter);
return view;
}
RecyclerView adapter
public class NewsExpandableAdapter extends RecyclerView.Adapter<NewsExpandableAdapter.ViewHolder> {
private Context context;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView nameTextView;
public ImageView messageButton;
public ViewHolder(View itemView) {
super(itemView);
nameTextView = (TextView) itemView.findViewById(R.id.title);
messageButton = (ImageView) itemView.findViewById(R.id.image);
}
}
private ArrayList<Model> mContacts;
public NewsExpandableAdapter(Context c, ArrayList<Model> contacts) {
this.context = c;
mContacts = (ArrayList<Model>) contacts;
}
#Override
public NewsExpandableAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View contactView = inflater.inflate(R.layout.expandable_list_item, parent, false);
ViewHolder viewHolder = new ViewHolder(contactView);
return viewHolder;
}
#Override
public void onBindViewHolder(NewsExpandableAdapter.ViewHolder viewHolder, int position) {
Model contact = mContacts.get(position);
TextView textView = viewHolder.nameTextView;
textView.setText(contact.getTitle());
ImageView image= viewHolder.messageButton;
Picasso.with(viewHolder.itemView.getContext())
.load(contact.getLink())
.into(image);
}
#Override
public int getItemCount() {
return mContacts.size();
}
}
Fragment XML
<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"
tools:context="com.example.sample">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="100dp"
android:id="#+id/expandableListView">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
RecyclerView XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/image"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/title"/>
</LinearLayout>
AsyncTask
public class Directory extends AsyncTask<Void, Void, String> {
String desc;
#Override
protected String doInBackground(Void... params) {
try {
Document document = Jsoup.connect(url + dateFormatYear.format(date)).get();
listing_latest = document.select(".title.page_title");
for (Element listing0 : listing_latest) {
Element hmonth = listing0.select("h3").first();
if(hmonth.text().equals(dateFormatMonth.format(date))){
month = hmonth.text();
hname = hmonth.parent().parent().parent().nextElementSibling();
releaseList = hname.select(".base_header.tc.m6");
for(Element block : releaseList){
name = block.select(".one_line.fs11 a").first();
image = block.select(".base_inner.h244.loading a img").first();
if(name != null){
hn = name.attr("title");
}
if(image != null){
hi = image.attr("abs:src");
}
m_hparts.add(new Model(hi));
}
}
}
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
hadapter.notifyDataSetChanged();
m_hparts.add(new Model(hi));
}
}
Edit: I put the new Directory().execute(); in the onCreate() Still could not get data.
You need to add all of the new items to the data source, and then call notifyDataSetChanged() after the data source has been updated.
Make the return value of doInBackground() a list of your items, and return it to onPostExecute(). You will need to change the parameters of the AsyncTask generics and the parameter passed into onPostExecute() as well.
Assuming your data source looks like this:
List<Model> m_hparts = new ArrayList<>();
You could do something like this:
public class Directory extends AsyncTask<Void, Void, List<Model>> {
String desc;
#Override
protected List<Model> doInBackground(Void... params) {
//added:
List<Model> parts = new ArrayList<>();
try {
Document document = Jsoup.connect(url + dateFormatYear.format(date)).get();
listing_latest = document.select(".title.page_title");
for (Element listing0 : listing_latest) {
Element hmonth = listing0.select("h3").first();
if(hmonth.text().equals(dateFormatMonth.format(date))){
month = hmonth.text();
hname = hmonth.parent().parent().parent().nextElementSibling();
releaseList = hname.select(".base_header.tc.m6");
for(Element block : releaseList){
name = block.select(".one_line.fs11 a").first();
image = block.select(".base_inner.h244.loading a img").first();
if(name != null){
hn = name.attr("title");
}
if(image != null){
hi = image.attr("abs:src");
}
//m_hparts.add(new Model(hi));
//do this instead:
parts.add(new Model(hi));
}
}
}
} catch (IOException e1) {
e1.printStackTrace();
}
//return null;
//do this instead:
return parts;
}
#Override
protected void onPostExecute(List<Model> parts) {
super.onPostExecute(result);
for (Model h: parts) {
m_hparts.add(h);
}
hadapter.notifyDataSetChanged();
}
}
Related
I have a ListView with a custom adapter. Every row has an ImageView that I render using a Bitmap, but my current code blocks the UI thread as I am using get() after executing my AsyncTask that downloads the bitmaps. I would like to change my code and access the imageViews in the onPostExecute() or something similar. So that the rows already display without waiting for all sprites to load.
Adapter class (download is triggered here)
public class PokemonAdapter extends ArrayAdapter<PokemonPOJO> implements View.OnClickListener{
private ArrayList<PokemonPOJO> dataSet;
Context mContext;
private int lastPosition = -1;
// View lookup cache
private static class ViewHolder {
TextView txtName;
TextView txtCP;
TextView txtGenderShiny;
ImageView sprite;
Button btnDelete;
}
public PokemonAdapter(ArrayList<PokemonPOJO> data, Context context) {
super(context, R.layout.row_pokemon, data);
this.dataSet = data;
this.mContext=context;
}
#Override
public void onClick(View v) {
int position=(Integer) v.getTag();
Object object= getItem(position);
PokemonPOJO dataModel=(PokemonPOJO)object;
switch (v.getId())
{
case R.id.btn_delete:
FirebaseDatabase.getInstance().getReference("pokemons").child(dataModel.getUid()).removeValue();
Toast.makeText(getContext(), "Pokemon removed!", Toast.LENGTH_SHORT).show();
this.remove(dataModel);
break;
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
PokemonPOJO dataModel = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
ViewHolder viewHolder; // view lookup cache stored in tag
final View result;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.row_pokemon, parent, false);
viewHolder.txtName = (TextView) convertView.findViewById(R.id.text_name);
viewHolder.txtCP = (TextView) convertView.findViewById(R.id.text_cp);
viewHolder.txtGenderShiny = (TextView) convertView.findViewById(R.id.text_gendershiny);
viewHolder.sprite = (ImageView) convertView.findViewById(R.id.img_sprite);
viewHolder.btnDelete = (Button)convertView.findViewById(R.id.btn_delete);
result=convertView;
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
result=convertView;
}
lastPosition = position;
viewHolder.txtName.setText(dataModel.getName());
viewHolder.txtCP.setText("CP: " + Integer.toString(dataModel.getCP()));
viewHolder.txtGenderShiny.setText(dataModel.getGender() + (dataModel.isShiny() ? " (Shiny)" : ""));
viewHolder.btnDelete.setOnClickListener(this);
try {
Bitmap bm = new DownloadImageTask().execute(dataModel.getSpriteUrl()).get();
viewHolder.sprite.setImageBitmap(bm);
} catch (Exception e) {
e.printStackTrace();
}
viewHolder.btnDelete.setTag(position);
// Return the completed view to render on screen
return convertView;
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
#Override
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap bm = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
bm = BitmapFactory.decodeStream(in);
} catch (Exception e) {
e.printStackTrace();
}
return bm;
}
}
Fragment with ListView
public class MyPokemonFragment extends Fragment {
private FirebaseAuth auth;
private DatabaseReference pokemonDb;
private TextView text_noPokemon;
private ListView listViewPokemon;
private static PokemonAdapter adapter;
private populateListViewTask populateListView;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_mypokemon,null);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
auth = FirebaseAuth.getInstance();
listViewPokemon = view.findViewById(R.id.list_pokemon);
text_noPokemon= view.findViewById(R.id.text_noPokemon);
Query getUserPokemon = FirebaseDatabase.getInstance().getReference("pokemons").orderByChild("userUid").equalTo(auth.getCurrentUser().getUid());
getUserPokemon.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if(!snapshot.hasChildren()) {
text_noPokemon.setText("You have not added any Pokémon yet.");
}
else {
TreeMap<String, Pokemon> pokemons = new TreeMap<>();
for (DataSnapshot pokemon : snapshot.getChildren()) {
pokemons.put(pokemon.getKey(), pokemon.getValue(Pokemon.class));
}
populateListView = new populateListViewTask();
populateListView.execute(pokemons);
}
}
#Override
public void onCancelled(DatabaseError databaseError) { }
});
}
#Override
public void onDestroy() {
super.onDestroy();
if(populateListView != null && populateListView.getStatus() == AsyncTask.Status.RUNNING)
populateListView.cancel(true);
}
private class populateListViewTask extends AsyncTask<TreeMap<String, Pokemon>, Void, ArrayList<PokemonPOJO>> {
#Override
protected ArrayList<PokemonPOJO> doInBackground(TreeMap<String, Pokemon>... maps) {
ArrayList<PokemonPOJO> pojos = new ArrayList<>();
HttpURLConnection connection = null;
BufferedReader reader = null;
Iterator it = maps[0].entrySet().iterator();
while(it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
Pokemon p = (Pokemon)pair.getValue();
try {
URL url = new URL("https://pokeapi.co/api/v2/pokemon/" + p.getPokedexNr() + "/");
connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream stream = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(stream));
StringBuffer buffer = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null) {
buffer.append(line + "\n");
}
JSONObject j = new JSONObject(buffer.toString());
String name = j.getString("name");
String spriteUrl = (p.isShiny() ? j.getJSONObject("sprites").getString("front_shiny") : j.getJSONObject("sprites").getString("front_default"));
PokemonPOJO pojo = new PokemonPOJO((String)pair.getKey(), p.getPokedexNr(), name, spriteUrl, p.isShiny(), p.getGender(), p.getCP());
pojos.add(pojo);
} catch (Exception e) {
e.printStackTrace();
} finally {
connection.disconnect();
try {
if (reader != null)
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return pojos;
}
#Override
protected void onPostExecute (ArrayList < PokemonPOJO > pojos) {
adapter = new PokemonAdapter(pojos, getContext());
listViewPokemon.setAdapter(adapter);
}
}
}
Pokemon row XML
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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="wrap_content"
android:layout_margin="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/img_sprite"
android:layout_width="96dp"
android:layout_height="96dp"
android:scaleType="fitCenter" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="left|center_vertical"
android:orientation="vertical">
<TextView
android:id="#+id/text_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#android:color/black"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="#+id/text_cp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp" />
<TextView
android:id="#+id/text_gendershiny"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="end"
android:orientation="vertical">
<Button
android:id="#+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="#color/colorPrimary"
android:text="DELETE"
android:textColor="#ffffff"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
You are having performance issues because you are calling the get() method on your AsyncTask. The get() method basically causes the main thread to wait until the code in the AsyncTask completes execution before the main thread continues executing other instructions. Why Google added this method is curious to say the least. So do this to fix your code.
Create a new Java class file. Name the file "DownloadImageTask" and add this code:
public interface DownloadImageListener {
void onCompletedImageDownload(Bitmap bm);
}
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
private static final String TAG = DownloadImageTask.class.getSimpleName();
private DownloadImageListener mListener;
private String imageUrl = "";
public DownloadImageTask(String imageUrl, DownloadImageListener listener){
this.imageUrl = imageUrl;
this.mListener = listener;
}
#Override
protected Bitmap doInBackground(String... urls) {
Bitmap bm = null;
try {
InputStream in = new java.net.URL(imageUrl).openStream();
bm = BitmapFactory.decodeStream(in);
} catch (Exception e) {
e.printStackTrace();
}
return bm;
}
protected void onPostExecute(Bitmap bm) {
mListener.onCompletedImageDownload(bm);
}
}
If you have any issues adding the public interface to the "DownloadImageTask" Java file just create a separate Java file name "DownloadImageListener" and put the interface code in there.
Set your code to query the AsyncTask.
Change the Adapter code inside your getView() from this:
try {
Bitmap bm = new DownloadImageTask().execute(dataModel.getSpriteUrl()).get();
viewHolder.sprite.setImageBitmap(bm);
} catch (Exception e) {
e.printStackTrace();
}
to this:
try {
DownloadImageListener listener = new DownloadImageListener() {
#Override
public void onCompletedImageDownload(Bitmap bm) {
if(bm != null){
viewHolder.sprite.setImageBitmap(bm);
}
}
};
String imageUrl = dataModel.getSpriteUrl();
DownloadImageTask downloadImageTask = new DownloadImageTask(imageUrl, listener);
downloadImageTask.execute();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
This allows your AsyncTask to execute and when the Bitmap is returned the listener is triggered in the onPostExecute() method sending the Bitmap to your ListView in the onCompletedImageDownload() callback method.
Additional Info:
To improve performance even further you could create a caching model to save and retrieve images from the device if you have already downloaded them in the past. But that requires some really advanced techniques--and gets really tricky when images you wish to download might change from time to time.
I have a Navigation menu with nav menu on it. When clicked on each nav menu, the specific fragment is opened.For example, when I click on Words nav menu, words item display with recyclerView items on it. I'm fetching data from offline and external SQLite database and display on recyclerView items. Now I want to fetch data in another thread, NOT in the main thread, because I want increase loading speed data and app performance. But I don't know how to do this. please help me with a code. I read the same subject on the internet, but I still now have my issue.
this is my AllWordsFragment
public class AllWordsFragment extends Fragment {
private List<WordsList> wordsLists = new ArrayList<>();
private Cursor cursor;
ProgressBar progressBar;
RecyclerView recyclerView;
AllWordsAdapter allWordsAdapter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.all_words_fragment, container, false);
progressBar = view.findViewById(R.id.progressBar);
progressBar.setMax(600);
allWordsAdapter = new AllWordsAdapter(getActivity(), wordsLists);
allWordsAdapter.notifyDataSetChanged();
recyclerView = view.findViewById(R.id.recyclerViewAllWords);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(allWordsAdapter);
loadingWords();
return view;
}
private void loadingWords() {
WordDatabase wordDatabase = new WordDatabase(getActivity());
try {
wordDatabase.createDatabase();
wordDatabase.openDatabase();
} catch (SQLiteException e) {
e.printStackTrace();
}
try {
cursor = wordDatabase.QueryData("SELECT Word, Definition, Example, WordList, ImageWord FROM Words");
if (cursor != null && cursor.moveToFirst()) {
do {
WordsList wordList = new WordsList();
wordList.setWordTitle(cursor.getString(0));
wordList.setDefinition(cursor.getString(1));
wordList.setExample(cursor.getString(2));
wordList.setVocubList(cursor.getString(3));
wordList.setImageWord(cursor.getString(4));
wordsLists.add(wordList);
} while (cursor.moveToNext());
wordDatabase.close();
}
} catch (SQLiteException w) {
w.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
and this is my AllWordsAdapter
public class AllWordsAdapter extends RecyclerView.Adapter {
private int lastPosition = -1;
protected Context context;
private List<WordsList> wordsListList = new ArrayList<>();
public AllWordsAdapter(Context context, List<WordsList> wordsListList) {
this.context = context;
this.wordsListList = wordsListList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.all_words_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
WordsList wordsList = wordsListList.get(position);
holder.wordTitle.setText(wordsList.getWordTitle());
holder.definitionWord.setText(Html.fromHtml(wordsList.getDefinition()));
holder.exampleWord.setText(Html.fromHtml(wordsList.getExample()));
holder.labelWordList.setLabelText(wordsList.getVocubList());
//get image from assets with Glide.
String pathImage = wordsList.getImageWord();
String assetsPath = "file:///android_asset/";
Glide.with(context)
.asBitmap()
.load(Uri.parse(assetsPath + "" + pathImage))
.into(holder.wordImage);
Log.d("path", assetsPath + "" + pathImage);
Typeface headerFont = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto-Bold.ttf");
holder.wordTitle.setTypeface(headerFont);
Typeface customFont = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto-Italic.ttf");
holder.exampleWord.setTypeface(customFont);
holder.definitionWord.setTypeface(customFont);
//cal animation function
setAnimation(holder.itemView, position);
holder.relativeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(context, AllWordsDetails.class);
intent.putExtra("word", holder.wordTitle.getText().toString());
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return wordsListList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private CircleImageView wordImage;
private LabelTextView labelWordList;
private TextView wordTitle, definitionWord, exampleWord;
private RelativeLayout relativeLayout;
public ViewHolder(View itemView) {
super(itemView);
wordTitle = itemView.findViewById(R.id.allWordTitle);
wordImage = itemView.findViewById(R.id.circleHeaderImage);
exampleWord = itemView.findViewById(R.id.exampleAllWord);
definitionWord = itemView.findViewById(R.id.definitionAllWord);
labelWordList = itemView.findViewById(R.id.labelWordList);
relativeLayout = itemView.findViewById(R.id.relativeAllWords);
}
}
private void setAnimation(View viewToAnimation, int position) {
// If the bound view wasn't previously displayed on screen, it's animated
if (position > lastPosition) {
ScaleAnimation scaleAnimation = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
viewToAnimation.startAnimation(scaleAnimation);
lastPosition = position;
}
}
}
I know must be use AsyncTask and do this in background, but I don't know how do this ? Please help me with a code. Thanks .
create an AsyncTask class inside your class:
class WordLoaderTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
loadingWords();
}
protected void onPostExecute(Void param) {
allWordsAdapter.notifyDataSetChanged();
}
}//asyncClass
replace calling loadingWords() in onCreate() with this line:
new WordLoaderTask().execute();
if you (for some reason or a way of using app) start getting duplicates in your ListView, then add wordsLists.clear(); as first line inside the do{} in loadingWords() method
Try like this
public class AllWordsFragment extends Fragment {
private List<WordsList> wordsLists = new ArrayList<>();
private Cursor cursor;
ProgressBar progressBar;
RecyclerView recyclerView;
AllWordsAdapter allWordsAdapter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.all_words_fragment, container, false);
progressBar = view.findViewById(R.id.progressBar);
progressBar.setMax(600);
allWordsAdapter = new AllWordsAdapter(getActivity(), wordsLists);
allWordsAdapter.notifyDataSetChanged();
recyclerView = view.findViewById(R.id.recyclerViewAllWords);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(allWordsAdapter);
loadingWords();
return view;
}
private static LoadWordsTask extends AsyncTask<Void, Void, List<WordsList>> {
private Context context;
private AllWordsAdapter adapter;
private List<WordsList> wordsLists;
public LoadWordsTask(Context context, AllWordsAdapter adapter, List<WordsList> wordsLists) {
this.context = context;
this.adapter = adapter;
this.wordsLists = wordsLists;
}
#Override
public List<WordsList> doInBackground() {
List<WordsList> data = new ArrayList<>();
WordDatabase wordDatabase = new WordDatabase(getActivity());
try {
wordDatabase.createDatabase();
wordDatabase.openDatabase();
} catch (SQLiteException e) {
e.printStackTrace();
}
try {
cursor = wordDatabase.QueryData("SELECT Word, Definition, Example, WordList, ImageWord FROM Words");
if (cursor != null && cursor.moveToFirst()) {
do {
WordsList wordList = new WordsList();
wordList.setWordTitle(cursor.getString(0));
wordList.setDefinition(cursor.getString(1));
wordList.setExample(cursor.getString(2));
wordList.setVocubList(cursor.getString(3));
wordList.setImageWord(cursor.getString(4));
data.add(wordList);
} while (cursor.moveToNext());
wordDatabase.close();
}
} catch (SQLiteException w) {
w.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
return data;
}
#Override
public void onPostExecute(List<WordsList> data) {
this.wordsLists.addAll(data);
this.adapter.notifyDataSetChanged();
}
}
}
I have a recyclerview in my project and data not bind to the recyclerview. I am getting data from the server and data correctly come. I put a Toast in the Adapter Class and it is working. I cannot figure out the problem.
Activity Class....
public class ViewDealerCompln extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_dealer_complain);
typeface = Typeface.createFromAsset(getAssets(), "productsans.ttf");
recyclerView = (RecyclerView) findViewById(R.id.com_recyclerView);
compList = new ArrayList<>();
toolbar = (Toolbar) findViewById(R.id.com_view_app_bar);
TextView mTitle = (TextView) toolbar.findViewById(R.id.toolbar_title);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle("");
mTitle.setText("Complain History");
mTitle.setTypeface(typeface);
repID = DealerListAdapter.getRepID();
dealerID = DealerListAdapter.getDealerID();
mLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(adcAdapter);
getData();
}
private void getData() {
String tag_string_req = "req_data";
request = new StringRequest(Request.Method.POST, AppConfig.URL_JSON_GETCOMPLAIN, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
if (jsonObject.names().get(0).equals("feed")) {
JSONArray jsonFeedArray = jsonObject.getJSONArray("feed");
if (jsonFeedArray.length() > 0) {
for (int i = 0; i < jsonFeedArray.length(); i++) {
JSONObject currentObject = jsonFeedArray.getJSONObject(i);
String namemm = currentObject.getString("compId");
compList.add(namemm);
}
adcAdapter = new ViewDealerComplainAdapter(ViewDealerCompln.this, compList);
} else {
Toast.makeText(getApplicationContext(), "No data Available!", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getApplicationContext(), "Invalid Response from the server!", Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
};
}
}
Adapter class...
public class ViewDealerComplainAdapter extends RecyclerView.Adapter<ViewDealerComplainAdapter.ItemViewHolder> {
private LayoutInflater inflater;
private ArrayList<String> subList;
private Context context;
public ViewDealerComplainAdapter(Context context, ArrayList<String> a) {
this.context = context;
inflater = LayoutInflater.from(context);
this.subList = a;
Toast.makeText(context, subList.toString(), Toast.LENGTH_SHORT).show();
}
#Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.custom_complain_list_row, parent, false);
ViewDealerComplainAdapter.ItemViewHolder holder = new ViewDealerComplainAdapter.ItemViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
holder.title.setText(subList.get(position));
}
#Override
public int getItemCount() {
return subList.size();
}
public class ItemViewHolder extends RecyclerView.ViewHolder {
private TextView title;
public ItemViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.compHID);
}
}
}
custom row layout...
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp">
<TextView
android:id="#+id/compHID"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:padding="10dp" />
</LinearLayout>
main layout...
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/com_view_app_bar"
layout="#layout/app_toolbar_send_complain"></include>
<android.support.v7.widget.RecyclerView
android:id="#+id/com_recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
In your activity, before this line recyclerView.setAdapter(adcAdapter);
write
adcAdapter=new ViewDealerComplainAdapter(this,new ArrayList<String>())
In the adapter create a function as follow
public void setData(ArrayList<String> data){
this.sublist=data;
}
Now, in the function getData()
after this
for (int i = 0; i < jsonFeedArray.length(); i++) {
JSONObject currentObject = jsonFeedArray.getJSONObject(i);
String namemm = currentObject.getString("compId");
compList.add(namemm);
}
write
adcAdapter.setData(compList);
adcAdapter.notifyDataSetChanged();
This will make give you the result right as rain
I think it is happening because you have set the adapter before creating object.
recyclerView.setAdapter(adcAdapter);
getData();
You have first set the adapter to recyclerView and then you call getData() method in which you create object of adcAdapter.
after the loading data from the server update RecyclerView
adcAdapter = new ViewDealerComplainAdapter(ViewDealerCompln.this, compList);
adcAdapter.notifyDataSetChanged();
another way
create a method in Adapter
public void setDataChange(List<Object> asList) {
this.List = asList;
//now, tell the adapter about the update
notifyDataSetChanged();
}
and
adcAdapter.setDataChange(list);
your adapter has two parameters context & arrayList, so while creating instance of adapter make sure you are passing both the values to adapter and set the same adapter to your RecyclerView.
mAdapter=new MAdapter(this,list);
mRecyclerView.setAdapter(mAdapter);
once you have data in your list then pass that list to adapter.
You are setting adcAdapter in to recyclerView before initialize it.
Change code as blow.
adcAdapter = new ViewDealerComplainAdapter(ViewDealerCompln.this, compList);
recyclerView.setAdapter(adcAdapter);
Also you need to call notifyDataSetChanged() on adapter to update recycleview when you get response.
adcAdapter.notifyDataSetChanged();
I am newbie to Android apps development.
I have a class, which contains RecyclerView.
public class ScreenOne extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private RecyclerView mRecyclerView;
private FilmSetAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
SwipeRefreshLayout mSwipeRefreshLayout;
RequestTask requestTask;
ArrayList<Film> listOfFilms;
ArrayList<Cinema> listOfCinemas;
ArrayList<String> cityIDs;
public ScreenOne() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.screen_one, container,
false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
mLayoutManager = new LinearLayoutManager(getActivity());
//mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(mLayoutManager);
//mRecyclerView.setItemAnimator(new DefaultItemAnimator());
listOfFilms = new ArrayList<>();
mAdapter = new FilmSetAdapter(listOfFilms);
mRecyclerView.setAdapter(mAdapter);
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh);
mSwipeRefreshLayout.setOnRefreshListener(this);
mSwipeRefreshLayout.setColorScheme(R.color.blue, R.color.cyan, R.color.grey_blue, R.color.lightblue);
try {
updateFilms();
} catch (InterruptedException e) {
e.printStackTrace();
}
return rootView;
}
private void updateFilms() throws InterruptedException {
requestTask = new RequestTask();
try {
requestTask.execute().get();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
private boolean isNetworkConnected() {
ConnectivityManager cm =
(ConnectivityManager) getActivity().getSystemService(getActivity().CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
return netInfo != null && netInfo.isConnectedOrConnecting();
}
#Override
public void onRefresh() {
Toast.makeText(getActivity(), R.string.refresh_started, Toast.LENGTH_SHORT).show();
mSwipeRefreshLayout.setRefreshing(true);
mAdapter.setDataset(new ArrayList<Film>());
mAdapter.notifyDataSetChanged();
try {
updateFilms();
} catch (InterruptedException e) {
e.printStackTrace();
}
DatabaseHandler database = new DatabaseHandler(getActivity());
mAdapter = new FilmSetAdapter(database.getAllFilms());
mRecyclerView.swapAdapter(mAdapter, false);
database.close();
mSwipeRefreshLayout.postDelayed(new Runnable() {
#Override
public void run() {
mSwipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), R.string.refresh_finished, Toast.LENGTH_SHORT).show();
}
}, 3000);
}
class RequestTask extends AsyncTask<Void, Void, Void> {
protected void getPreferenciesCity()
{
cityIDs = new ArrayList<>();
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
Set<String> selections = sharedPrefs.getStringSet("cities", null);
try {
Log.d("SHARED PREFERENCES CITY", selections.toString());
cityIDs.addAll(selections);
}catch (NullPointerException e){Log.d("SHARED PREFERENCES CITY", "SP are empty");}
}
#Override
protected void onPreExecute() {
super.onPreExecute();
listOfFilms = new ArrayList<>();
getPreferenciesCity();
}
#Override
protected Void doInBackground(Void... params) {
if(isNetworkConnected()) {
DatabaseHandler database = new DatabaseHandler(getActivity());
database.clearFilms();
try {
HttpHandler httpHandler = new HttpHandler();
listOfFilms = new ArrayList<>(httpHandler.listOfFilmsRequest(cityIDs));
listOfCinemas = new ArrayList<>(httpHandler.listOfCinemasRequest());
Film film;
Cinema cinema;
for (Iterator<Film> filmiterator = listOfFilms.iterator(); filmiterator.hasNext(); ) {
film = filmiterator.next();
if (database.isInDatabase(film.getID()))
database.updateFilm(film);
else
database.addFilm(film);
}
for (Iterator<Cinema> cinemaiterator = listOfCinemas.iterator(); cinemaiterator.hasNext(); ) {
cinema = cinemaiterator.next();
if (database.CinemaIsInDatabase(cinema.getIDCinema()))
database.updateCinema(cinema);
else
database.addCinema(cinema);
}
database.close();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
DatabaseHandler database = new DatabaseHandler(getActivity());
mAdapter.setDataset(database.getAllFilms());
mAdapter.notifyDataSetChanged();
}
}
}
It executes a method, which is placed in the AsyncTask class. This method downloads data from server, puts it to database first and after that sets the recycler view adapter. The problem is that sometimes text data are changed while scrolling it. See attached video.
Code of adapter is:
public class FilmSetAdapter extends RecyclerView.Adapter<FilmSetAdapter.ViewHolder>{
private ArrayList<Film> mDataset;
final static String LOG_TAG = "FilmSetAdapter";
public void setDataset(ArrayList<Film> allFilms) {
mDataset = new ArrayList<>(allFilms);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public static TextView filmTitle;
public TextView country;
public TextView ticketsFrom;
public TextView rating;
public TextView format;
public ImageView poster;
public int id;
public ViewHolder(final View itemView) {
super(itemView);
filmTitle = (TextView) itemView.findViewById(R.id.filmtitle);
country = (TextView)itemView.findViewById(R.id.country);
ticketsFrom = (TextView) itemView.findViewById(R.id.ticketsfrom);
rating = (TextView)itemView.findViewById(R.id.rating);
poster = (ImageView) itemView.findViewById(R.id.poster);
format = (TextView) itemView.findViewById(R.id.format);
format.setTypeface(null, Typeface.BOLD_ITALIC);
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cardview, parent, false);
final ViewHolder vh = new ViewHolder(v);
v.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
Log.d(LOG_TAG, "Нажата карточка. Вызов из activity "+v.toString());
Intent intent = new Intent(v.getContext(),FilmInfoActivity.class);
intent.putExtra("EXTRA_FILM_ID", vh.id);
v.getContext().startActivity(intent);
}
});
return vh;
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.filmTitle.setText(mDataset.get(position).getTitle().toString()+" ID="+mDataset.get(position).getID()+" POS="+position);
holder.country.setText(mDataset.get(position).getCountry());
holder.ticketsFrom.setText(holder.ticketsFrom.getContext().getString(R.string.tickets_from) +
mDataset.get(position).getRating().toString());
holder.rating.setText(holder.rating.getContext().getString(R.string.rating_kinopoisk) +
mDataset.get(position).getRating());
holder.id = mDataset.get(position).getID();
holder.format.setText(mDataset.get(position).getFormat());
Picasso.with(holder.poster.getContext()).
load(mDataset.get(position).getPoster()).
skipMemoryCache().
into(holder.poster);
}
#Override
public int getItemCount() {
if(mDataset != null)
return mDataset.size();
return 0;
}
public FilmSetAdapter(ArrayList<Film> myDataset) {
mDataset = new ArrayList<>(myDataset);
}
}
I have no clue on how to sort it out.
it seem to that there is a typo mistake in your code, look at that, you have in your ViewHolder:
public static TextView filmTitle;
But then you work with it like with your other fields:
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.filmTitle.setText(mDataset.get(position).getTitle() + ...);
...
}
A little advice for you as a newbie: just.. pay more attention to your code, and you'll be fine in programming :)
I'm migrating from ListView to an RecyclerView and last thing I'm stuck with is updating one of fields. My App has a simple list with some images found on internet. When you choose one, image gets downloaded to a phone for later offline viewing. Adapter gets data from SQLite databaseso when you tap on some image database gets updated (text changes from a "Tap here to Download" to a "Downloaded") and RecyclerView should follow.
I had same problem with ListView but there I just called populateList(); each time App updated db. I know it's not the ideal solution but it worked. Now I want to do it right with notifyDataSetChanged() or even better notifyItemChanged(position) but I can't get it working.
Anyway here's the code, sorry for being a little bit messy. This is just a test code (RecyclerView code is from samples):
public class RecyclerFragment extends Fragment {
private imgAdapter mAdapter;
private DatabaseHandler db;
private ProgressDialog mProgressDialog;
public RecyclerFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
db = new DatabaseHandler(getActivity());
List<Images> listdb = db.getImages();
mAdapter = new ImgAdapter(getActivity(), listdb);
db.close();
View view = inflater.inflate(R.layout.fragment_main, container, false);
RecyclerView recyclerView = (RecyclerView) view.findViewById(android.R.id.list);
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new GridLayoutManager(
getActivity(), getResources().getInteger(R.integer.list_columns)));
recyclerView.setAdapter(mAdapter);
return view;
}
private class ImgAdapter extends RecyclerView.Adapter<ViewHolder>
implements ItemClickListener {
public List<Images> mList;
private Context mContext;
public ImgAdapter(Context context, List<Images> listdb) {
super();
mContext = context;
mList = listdb;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.list_row1, parent, false);
return new ViewHolder(view, this);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
Images image = mList.get(position);
holder.mTitle.setText(image.getTitle());
holder.mDownloadStatus.setText(image.getDownloadStatus());
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemCount() {
return mList == null ? 0 : mList.size();
}
#Override
public void onItemClick(View view, int position) {
switch (position) {
case 0:
Log.v("POSITION 0:", " " + position);
break;
case 1:
/*
some logic nvm
*/
String imgurl = "http:/...";
String imagename = "Second image";
new GetImages(imgurl, imagename).execute();
/*
I've tried mAdapter.notitfy... , no luck aswell
This does nothing:
*/
notifyItemChanged(position);
notifyDataSetChanged();
break;
//....
}
}
}
private static class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
TextView mTitle;
TextView mDownloadStatus;
ItemClickListener mItemClickListener;
public ViewHolder(View view, ItemClickListener itemClickListener) {
super(view);
mTitle = (TextView) view.findViewById(R.id.text1);
mDownloadStatus = (TextView) view.findViewById(R.id.text2);
mItemClickListener = itemClickListener;
view.setOnClickListener(this);
}
#Override
public void onClick(View v) {
mItemClickListener.onItemClick(v, getPosition());
}
}
interface ItemClickListener {
void onItemClick(View view, int position);
}
/*
This is my AsyncTask class used for downloading image and updating db
I removed some code just to to make it cleaner
*/
private class GetImages extends AsyncTask<Object, Object, Object> {
private final String requestUrl;
private final String imagename_;
private String imagename;
public int numberofbits;
private GetImages(String requestUrl, String _imagename_) {
this.requestUrl = requestUrl;
this.imagename_ = _imagename_;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
//pDialog...
}
#Override
protected Object doInBackground(Object... objects) {
try {
URL url = new URL(requestUrl);
URLConnection conn = url.openConnection();
bitmap = BitmapFactory.decodeStream(conn.getInputStream());
numberofbits = bitmap.getByteCount();
} catch (Exception ignored) {
}
return null;
}
#Override
protected void onPostExecute(Object o) {
if (!ImageStorage.checkifImageExists(imagename)) {
ImageStorage.saveToSdCard(bitmap, imagename_);
}
if (numberofbits > 50) {
db = new DatabaseHandler(getActivity());
db.updateImages(new Images(dbid, "Downloaded", imagename_));
db.close();
//populateList(); -> THIS I USED FOR A LISTVIEW
Toast.makeText(getActivity(), "Downloaded!", Toast.LENGTH_SHORT).show();
mProgressDialog.dismiss();
//If image gets downloaded open it in Viewer class
Intent intent = new Intent(getActivity(), DisplayImage.class);
intent.putExtra("imagePath", path);
startActivity(intent);
} else {
//Toast.makeText(getActivity(), "Unable to download", Toast.LENGTH_SHORT).show();
mProgressDialog.dismiss();
}
}
}
}
Here's Images class
public class Images {
//private variables
private int _id;
private String _title;
private String _downloadstatus;
// Empty constructor
public Images () {
}
// constructor
public Images(int id, String title, String downloadstatus) {
this._id = id;
this._title = title;
this._downloadstatus = downloadstatus;
}
public int getID() {
return this._id;
}
public void setID(int id) {
this._id = id;
}
public String getTitle() {
return this._title;
}
public void setTitle(String title) {
this._title = title;
}
public String getDownloadStatus() {
return this._downloadstatus;
}
public void setDownloadStatus(String downloadstatus) {
this._downloadstatus = downloadstatus;
}
}
And here's an XML (I'm trying to update "text2"):
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:foreground="?attr/selectableItemBackground"
android:layout_height="wrap_content"
android:padding="8dp">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
>
<TextView
android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="#dimen/imagename"
android:textStyle="bold"
android:paddingStart="4sp"
android:paddingEnd="40sp" />
<TextView
android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/text1"
android:textSize="#dimen/downloaded"
android:paddingStart="4sp"
android:paddingEnd="1sp"
android:textColor="#color/graydownloaded" />
</RelativeLayout>
</FrameLayout>
Okay I finally got it working and It was actually quite simple...
//....
if (numberofbits > 50) {
db = new DatabaseHandler(getActivity());
db.updateImages(new Images(dbid, "Downloaded", imagename_));
//This is what I added:
List<Images> listdb = db.getImages();
mAdapter = new ImgAdapter(getActivity(), listdb);
recyclerView.setAdapter(mAdapter);
db.close();
//populateList(); -> THIS I USED FOR A LISTVIEW
Toast.makeText(getActivity(), "Downloaded!", Toast.LENGTH_SHORT).show();
mProgressDialog.dismiss();
//If image gets downloaded open it in Viewer class
Intent intent = new Intent(getActivity(), DisplayImage.class);
intent.putExtra("imagePath", path);
startActivity(intent);
} else {
//...
Try doing this: Override in your adapter the getItemId method like this
#Override
public long getItemId(int position) {
return mList.get(position).getID();
}
And add this line after recyclerView.setHasFixedSize(true) :
recyclerView.setHasStableId(true);
EDIT:
mAdapter.setHasStableId(true);
Let me know if that did the trick.