I don't know why I am getting this error.
I had implemented filterResults().
Actually, when I search first time in edit text and click on it, it works fine.
Then when I come back and search for some city again, it shows 2 same entries. I have added screenshots.
Then when I come back and search 3rd time I got 3 same entries for each city searched.
My java class:
package com.wordpress.myselfnikunj.cofighter;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.Volley;
import com.wordpress.myselfnikunj.cofighter.Adapters.CountryListAdapter;
import com.wordpress.myselfnikunj.cofighter.Model.CountryNamesModel;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class AffectedCountriesFragment extends Fragment {
EditText searchEditText;
ListView listView;
public static List<CountryNamesModel> countryNamesModelList = new ArrayList<>();
CountryNamesModel countryNamesModel;
CountryListAdapter countryListAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_affected_countries, container, false);
searchEditText = (EditText) view.findViewById(R.id.searchEditText);
listView = (ListView) view.findViewById(R.id.countriesListView);
countryListAdapter = new CountryListAdapter(getContext(), countryNamesModelList);
// listView.setAdapter(countryListAdapter);
fetchData();
searchEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
countryListAdapter.getFilter().filter(charSequence);
countryListAdapter.notifyDataSetChanged();
}
#Override
public void afterTextChanged(Editable editable) {
}
});
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Bundle position = new Bundle();
position.putInt("position", i);
Navigation.findNavController(view).navigate(R.id.action_affectedCountriesFragment_to_countryDetailsFragment, position);
}
});
return view;
}
private void fetchData() {
String Url = "https://corona.lmao.ninja/v2/countries/";
RequestQueue requestQueue = Volley.newRequestQueue(getContext());
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(
Url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.i("hehes","entered");
try {
Log.i("hehe", response.toString());
for (int i = 0; i < response.length(); i++) {
JSONObject jsonObject = response.getJSONObject(i);
String countryName = jsonObject.getString("country");
String cases = jsonObject.getString("cases");
String todayCases = jsonObject.getString("todayCases");
String deaths = jsonObject.getString("deaths");
String recovered = jsonObject.getString("recovered");
String todayDeaths = jsonObject.getString("todayDeaths");
String active = jsonObject.getString("active");
String critical = jsonObject.getString("critical");
JSONObject object = jsonObject.getJSONObject("countryInfo");
String flagUrl = object.getString("flag");
countryNamesModel = new CountryNamesModel(getContext(),flagUrl, countryName, cases, todayCases, deaths, todayDeaths, recovered, active, critical);
countryNamesModelList.add(countryNamesModel);
}
countryListAdapter.notifyDataSetChanged();
listView.setAdapter(countryListAdapter);
}catch (Exception e) {
Log.i("error", e.getMessage());
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.i("errors", error.getMessage());
Toast.makeText(getContext(), error.getMessage(), Toast.LENGTH_SHORT).show();
}
}
);
requestQueue.add(jsonArrayRequest);
}
My custom Adapter
package com.wordpress.myselfnikunj.cofighter.Adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bumptech.glide.Glide;
import com.wordpress.myselfnikunj.cofighter.AffectedCountriesFragment;
import com.wordpress.myselfnikunj.cofighter.Model.CountryNamesModel;
import com.wordpress.myselfnikunj.cofighter.R;
import com.wordpress.myselfnikunj.cofighter.UpdatesFragment;
import java.util.ArrayList;
import java.util.List;
public class CountryListAdapter extends ArrayAdapter<CountryNamesModel> {
private Context context;
private List<CountryNamesModel> countryModelList;
private List<CountryNamesModel> countryModelListFiltered;
public CountryListAdapter( Context context, List<CountryNamesModel> countryModelList) {
super(context, R.layout.countryitem, countryModelList);
this.context = context;
this.countryModelList = countryModelList;
this.countryModelListFiltered = countryModelList;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.countryitem, null, true);
TextView tvCountryName = view.findViewById(R.id.countryName);
ImageView imageView = view.findViewById(R.id.countryImageFlag);
tvCountryName.setText(countryModelListFiltered.get(position).getCountry());
Glide.with(context).load(countryModelListFiltered.get(position).getFlag()).into(imageView);
return view;
}
#Override
public int getCount() {
return countryModelListFiltered.size();
}
#Nullable
#Override
public CountryNamesModel getItem(int position) {
return countryModelListFiltered.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#NonNull
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults filterResults = new FilterResults();
if (charSequence == null
|| charSequence.length() == 0
) {
filterResults.count = countryModelList.size();
filterResults.values = countryModelList;
} else {
List<CountryNamesModel> resultsModel = new ArrayList<>();
String searchStr = charSequence.toString().toLowerCase();
for (CountryNamesModel itemsModel: countryModelList) {
if(itemsModel.getCountry().toLowerCase().contains(searchStr)) {
resultsModel.add(itemsModel);
}
filterResults.count = resultsModel.size();
filterResults.values = resultsModel;
}
}
return filterResults;
}
#Override
protected void publishResults(CharSequence charSequence, FilterResults results) {
countryModelListFiltered = (List<CountryNamesModel>) results.values;
UpdatesFragment.countryNamesModelList = (List<CountryNamesModel>) results.values;
//AffectedCountriesFragment.countryNamesModelList = (List<CountryNamesModel>) results.values;
notifyDataSetChanged();
}
};
return filter;
}
}
Screenshots
Second Time
Third Time
When you navigate to another fragment view of the AffectedCountriesFragment getting destroyed and public void onDestroyView() is called. When you return back public View onCreateView is called again. But your fragment was not totally destroyed and all of its variables still exist and are in the state they were left.
It means that when you navigate to the next fragment your countryNamesModelList stays filled and when you return back to the AffectedCountriesFragment the same filled list getting filled again resulting in duplicated entries.
What you can do?
Track the state of your fragment. Create a new class-level variable private View view; that will hold the view of your fragment.
// If null - must be initialized and returned in `onCreateView`
// If not null - must be returned in `onCreateView` immediately
// avoiding recreation of the same view (performance improvement)
private View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (view == null) {
// Inflate the layout for this fragment ONLY IF IT IS NULL
View view = inflater.inflate(R.layout.fragment_affected_countries, container, false);
searchEditText = (EditText) view.findViewById(R.id.searchEditText);
listView = (ListView) view.findViewById(R.id.countriesListView);
countryListAdapter = new CountryListAdapter(getContext(), countryNamesModelList);
// listView.setAdapter(countryListAdapter);
fetchData();
searchEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
countryListAdapter.getFilter().filter(charSequence);
countryListAdapter.notifyDataSetChanged();
}
#Override
public void afterTextChanged(Editable editable) {
}
});
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Bundle position = new Bundle();
position.putInt("position", I);
Navigation.findNavController(view).navigate(R.id.action_affectedCountriesFragment_to_countryDetailsFragment, position);
}
});
}
return view;
}
Make sure you are not missing something in an understanding of Activity and Fragment lifecycles. Here is a good example of their lifecycles.
Related
My scrolling list is displaying properly.
OnBindViewHolder is called for each item in initial display and when scrolling brings new items into view.
When I click on an item, I do see my ItemDetailsLookup and getItemDetails functions getting called, HOWEVER it doesn't call the OnBindViewHolder, and so setActivated isn't getting run.
The documentation for androidx.recyclerview.selection says "... When the user selects an item the library will record that in SelectionTracker then notify RecyclerView that the state of the item has changed. This will ultimately cause the value to be rebound by way of RecyclerView.Adapter#onBindViewHolder..." and I think I've done everything up to this point, but I must have missed something...
Any ideas?
Thanks
Here's my fragment:
package com.example.smartflashcards;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.selection.ItemDetailsLookup;
import androidx.recyclerview.selection.SelectionPredicates;
import androidx.recyclerview.selection.SelectionTracker;
import androidx.recyclerview.selection.StableIdKeyProvider;
import androidx.recyclerview.selection.StorageStrategy;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class StackSelectionFragment extends Fragment {
private static final String ARG_COLUMN_COUNT = "column-count";
private int mColumnCount = 1;
static SelectionTracker<Long> tracker;
public StackSelectionFragment() {
}
#SuppressWarnings("unused")
public static StackSelectionFragment newInstance(int columnCount) {
StackSelectionFragment fragment = new StackSelectionFragment();
Bundle args = new Bundle();
args.putInt(ARG_COLUMN_COUNT, columnCount);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_stack_selection_list, container, false);
File directory = getActivity().getFilesDir();
List<String> filesList = new ArrayList(Arrays.asList(directory.list()));
if (view instanceof RecyclerView) {
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
if (mColumnCount <= 1) {
recyclerView.setLayoutManager(new LinearLayoutManager(context));
} else {
recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
}
recyclerView.setAdapter(new MyItemRecyclerViewAdapter(filesList));
recyclerView.getAdapter().hasStableIds();
this.tracker = new SelectionTracker.Builder<Long>(
"stack_selector",
recyclerView,
new StableIdKeyProvider(recyclerView),
new MyDetailsLookup(recyclerView),
StorageStrategy.createLongStorage()
).withSelectionPredicate(SelectionPredicates.<Long>createSelectAnything()).build();
}
return view;
}
final class MyDetailsLookup extends ItemDetailsLookup {
private final RecyclerView mRecyclerView;
MyDetailsLookup(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
}
public #Nullable
ItemDetails getItemDetails(#NonNull MotionEvent e) {
View view = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if (view != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view);
if (holder instanceof MyItemRecyclerViewAdapter.ViewHolder) {
return ((MyItemRecyclerViewAdapter.ViewHolder)holder).getItemDetails();
}
}
return null;
}
}
}
Here's my adapter:
package com.example.smartflashcards;
import androidx.annotation.Nullable;
import androidx.recyclerview.selection.ItemDetailsLookup;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.smartflashcards.databinding.FragmentStackSelectionBinding;
import java.util.List;
public class MyItemRecyclerViewAdapter extends RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder> {
private final List<String> mValues;
public MyItemRecyclerViewAdapter(List<String> items) {
setHasStableIds(true);
mValues = items;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(FragmentStackSelectionBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = getItemId(position);
holder.mContentView.setText(mValues.get(position));
holder.mContentView.setActivated(StackSelectionFragment.tracker.isSelected((long) position));
}
#Override
public int getItemCount() {
return mValues.size();
}
#Override
public long getItemId(int position) {
return (long)position;
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final TextView mIdView;
public final TextView mContentView;
public Long mItem;
public ViewHolder(FragmentStackSelectionBinding binding) {
super(binding.getRoot());
mIdView = binding.itemNumber;
mContentView = binding.content;
}
#Override
public String toString() {
return super.toString() + " '" + mContentView.getText() + "'";
}
public ItemDetailsLookup.ItemDetails<Long> getItemDetails() {
return new ItemDetailsLookup.ItemDetails<Long>() {
#Override
public int getPosition() {
return getAbsoluteAdapterPosition();
}
#Nullable
#Override
public Long getSelectionKey() {
return mItem;
}
};
}
}
}
By following the example at https://github.com/Thumar/recyclerview-selection/tree/master/app
I was able to get it working. See my working code below.
But I still don't know which of the changes made it work and why.
If anyone figures out what was causing my problem, please post a comment or answer.
Any and all other feedback is welcome as well.
THANKS
Here's my new fragment:
package com.example.smartflashcards;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.selection.ItemDetailsLookup;
import androidx.recyclerview.selection.Selection;
import androidx.recyclerview.selection.SelectionPredicates;
import androidx.recyclerview.selection.SelectionTracker;
import androidx.recyclerview.selection.StableIdKeyProvider;
import androidx.recyclerview.selection.StorageStrategy;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class StackSelectionFragment extends Fragment {
// TODO: Customize parameter argument names
private static final String ARG_COLUMN_COUNT = "column-count";
// TODO: Customize parameters
private int mColumnCount = 1;
SelectionTracker<Long> tracker; //TODO: add private?
private StackSelectionListener listener;
public interface StackSelectionListener {
public void onSelectStack(String stack);
}
String selections = "";
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public StackSelectionFragment() {
}
// TODO: Customize parameter initialization
#SuppressWarnings("unused")
public static StackSelectionFragment newInstance(int columnCount) {
StackSelectionFragment fragment = new StackSelectionFragment();
Bundle args = new Bundle();
args.putInt(ARG_COLUMN_COUNT, columnCount);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_stack_selection_list, container, false);
File directory = getActivity().getFilesDir();
List<String> filesList = new ArrayList(Arrays.asList(directory.list()));
// Set the adapter
if (view instanceof RecyclerView) {
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
if (mColumnCount <= 1) {
recyclerView.setLayoutManager(new LinearLayoutManager(context));
} else {
recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
}
MyItemRecyclerViewAdapter adapter = new MyItemRecyclerViewAdapter(filesList);
recyclerView.setAdapter(adapter);
adapter.hasStableIds();
this.tracker = new SelectionTracker.Builder<Long>(
"stack_selector",
recyclerView,
new StableIdKeyProvider(recyclerView),
new MyDetailsLookup(recyclerView),
StorageStrategy.createLongStorage()
).withSelectionPredicate(SelectionPredicates.<Long>createSelectAnything()).build();
adapter.injectTracker(this.tracker);
SelectionTracker.SelectionObserver<Long> observer = new SelectionTracker.SelectionObserver<Long>() {
#Override
public void onSelectionChanged() {
super.onSelectionChanged();
//TODO: replace this placeholder action with something useful
selections = "";
tracker.getSelection().forEach(item -> selections += adapter.getContent(item.intValue()));//selections += item);
//listener.onSelectStack("selections"); //This causes an error???
Context context = getContext();
Toast toast = Toast.makeText(context, selections, Toast.LENGTH_LONG);
toast.show();
}
};
this.tracker.addObserver(observer);
}
return view;
}
final class MyDetailsLookup extends ItemDetailsLookup {
private final RecyclerView mRecyclerView;
MyDetailsLookup(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
}
public #Nullable
ItemDetails getItemDetails(#NonNull MotionEvent e) {
View view = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if (view != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view);
if (holder instanceof MyItemRecyclerViewAdapter.ViewHolder) {
return ((MyItemRecyclerViewAdapter.ViewHolder)holder).getItemDetails();
}
}
return null;
}
}
Here's my new adapter:
package com.example.smartflashcards;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.selection.ItemDetailsLookup;
import androidx.recyclerview.selection.SelectionTracker;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
public class MyItemRecyclerViewAdapter extends RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder> {
private final List<String> mValues;
private SelectionTracker<Long> selectionTracker;
public MyItemRecyclerViewAdapter(List<String> items) {
setHasStableIds(true); //TODO: remove redundancy between this and "recyclerView.getAdapter().hasStableIds()" in the Fragment
mValues = items;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_stack_selection, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
String item = this.mValues.get(position);
holder.bind(item, selectionTracker.isSelected((long) position));
}
#Override
public int getItemCount() {
return mValues.size();
}
#Override
public long getItemId(int position) {
return (long)position;
}
public String getContent(int position) {
return mValues.get(position);
}
public void injectTracker(SelectionTracker<Long> tracker)
{
this.selectionTracker = tracker;
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final TextView mContentView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
mContentView = itemView.findViewById(R.id.content);
}
#Override
public String toString() {
return super.toString() + " '" + mContentView.getText() + "'";
}
public void bind(String item, Boolean activate) {
this.mContentView.setText(item);
itemView.setActivated(activate); //TODO: understand how "itemView" exists outside of the constructor
}
public ItemDetailsLookup.ItemDetails<Long> getItemDetails() {
return new ItemDetailsLookup.ItemDetails<Long>() {
#Override
public int getPosition() {
return getAbsoluteAdapterPosition();
}
#Nullable
#Override
public Long getSelectionKey() {
return (long) getAbsoluteAdapterPosition();
}
};
}
}
}
I'm a beginner in Java and I'm trying to create a listener in my DialogFragment to notice my fragment that some book was removed. When the user removes a book, I want to call the removeBook method in BookFragment and update the recyclerview.
Here is my BookDialogFragment.java:
package com.compose.dietapp.ui.books;
import android.app.Dialog;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import java.util.Objects;
public class BookDialogFragment extends DialogFragment {
private final String nameToDelete;
private final int position;
private static String MESSAGE_TO_DIALOG_FRAGMENT;
public BookDialogFragment(String nameToDelete, int position) {
this.nameToDelete = nameToDelete;
this.position = position;
}
public static BookDialogFragment newInstance(String title, String nameToDelete, int position) {
BookDialogFragment frag = new BookDialogFragment(nameToDelete, position);
if (Objects.equals(nameToDelete, "none")) {
MESSAGE_TO_DIALOG_FRAGMENT = "Book removed!";
} else {
MESSAGE_TO_DIALOG_FRAGMENT = "Are you sure you want to remove the book '"
+ nameToDelete
+ "'?";
}
Bundle args = new Bundle();
args.putString("title", title);
frag.setArguments(args);
return frag;
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
assert getArguments() != null;
String title = getArguments().getString("title");
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
alertDialogBuilder.setTitle(title);
alertDialogBuilder.setMessage(MESSAGE_TO_DIALOG_FRAGMENT);
if (Objects.equals(MESSAGE_TO_DIALOG_FRAGMENT, "Book removed!")) {
alertDialogBuilder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (dialog != null) {
dialog.dismiss();
}
}
});
} else {
alertDialogBuilder.setPositiveButton("Remove", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// here I want to callback my fragment
BookFragment deleteBook = new BookFragment();
deleteBook.removeBook(nameToDelete, position);
Bundle result = new Bundle();
result.putBoolean("value", true);
getParentFragmentManager().setFragmentResult("removed", result);
}
});
alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (dialog != null) {
dialog.dismiss();
}
}
});
}
return alertDialogBuilder.create();
}
#Override
public void onStart() {
super.onStart();
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEGATIVE)
.setTextColor(Color.rgb(128, 128, 128));
}
}
And here is my BookFragment.java:
package com.compose.dietapp.ui.books;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.compose.dietapp.R;
import com.compose.dietapp.database.DatabaseAccess;
import com.compose.dietapp.databinding.FragmentBookBinding;
import java.util.ArrayList;
public class BookFragment extends Fragment{
#SuppressLint("StaticFieldLeak")
private static Activity activity;
private RecyclerView recyclerView;
private final ArrayList<Book> itensBook = new ArrayList<Book>();
private BookAdapter bookAdapter;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
activity = this.getActivity();
View v = inflater.inflate(R.layout.fragment_book, container, false);
recyclerView = (RecyclerView) v.findViewById(R.id.book_recycler);
instanciateBooks();
return v;
}
private void instanciateBooks() {
ArrayList<ArrayList<String>> bookDatabaseValues = getDatabaseData();
if (recyclerView != null && bookDatabaseValues != null) {
createItemBook(bookDatabaseValues);
createRecyclerViewBook();
}
}
public void removeBook(String nameToDelete, int position) {
getChildFragmentManager().setFragmentResultListener("removed",
this, new FragmentResultListener() {
#Override
public void onFragmentResult(#NonNull String requestKey, #NonNull Bundle bundle) {
String result = bundle.getString("value");
Log.i("result", result);
}
});
}
private void createRecyclerViewBook() {
recyclerView.setHasFixedSize(true);
bookAdapter = new BookAdapter(activity, itensBook);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(activity,
LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(bookAdapter);
}
private void createItemBook(ArrayList<ArrayList<String>> bookDatabaseValues) {
for (int i = 0; i < bookDatabaseValues.size(); i++) {
itensBook.add(new Book(
String.valueOf(bookDatabaseValues.get(i).get(0)),
String.valueOf(bookDatabaseValues.get(i).get(1)),
String.valueOf(bookDatabaseValues.get(i).get(2)),
String.valueOf(bookDatabaseValues.get(i).get(3)),
String.valueOf(bookDatabaseValues.get(i).get(4))
));
}
}
public static ArrayList<ArrayList<String>> getDatabaseData() {
if (activity != null) {
return getArrayListsFromDatabase();
}
return null;
}
private static ArrayList<ArrayList<String>> getArrayListsFromDatabase() {
DatabaseAccess databaseAccess = openDatabase();
ArrayList<ArrayList<String>> books = databaseAccess.getBook();
closeDatabase(databaseAccess);
return books;
}
private static void closeDatabase(DatabaseAccess databaseAccess) {
databaseAccess.close();
}
#NonNull
private static DatabaseAccess openDatabase() {
DatabaseAccess databaseAccess = DatabaseAccess.getInstance(activity);
databaseAccess.open();
return databaseAccess;
}
}
Here is my BookAdapter.java:
package com.compose.dietapp.ui.books;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView;
import com.compose.dietapp.R;
import java.io.InputStream;
import java.util.ArrayList;
public class BookAdapter extends RecyclerView.Adapter<BookViewHolder> {
public static FragmentManager supportFragment;
private final Context context;
private final ArrayList<Book> itens;
public BookAdapter(Context context, ArrayList<Book> itens) {
this.context = context;
this.itens = itens;
}
#NonNull
#Override
public BookViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_recycler_book, parent, false);
BookViewHolder viewHolder = new BookViewHolder(view);
supportFragment = ((AppCompatActivity)context).getSupportFragmentManager();
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull BookViewHolder bookViewHolder, int position) {
Book book = itens.get(position);
bookViewHolder.nome.setText(book.getName());
bookViewHolder.bookType.setText(book.getBookType());
imageInstanciate(bookViewHolder);
}
private void imageInstanciate(#NonNull BookViewHolder bookViewHolder) {
new DownloadImageFromInternet((ImageView) bookViewHolder.bookImage)
.execute("https://pbs.twimg.com/profile_images/630285593268752384/iD1MkFQ0.png");
}
private class DownloadImageFromInternet extends AsyncTask<String, Void, Bitmap> {
ImageView imageView;
public DownloadImageFromInternet(ImageView imageView) {
this.imageView = imageView;
}
protected Bitmap doInBackground(String... urls) {
String imageURL = urls[0];
Bitmap bimage = null;
try {
InputStream in = new java.net.URL(imageURL).openStream();
bimage = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error Message", e.getMessage());
e.printStackTrace();
}
return bimage;
}
protected void onPostExecute(Bitmap result) {
imageView.setImageBitmap(result);
}
}
#Override
public int getItemCount() {
return itens.size();
}
}
Here is my BookViewHolder.java:
package com.compose.dietapp.ui.books;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView;
import com.compose.dietapp.R;
public class BookViewHolder extends RecyclerView.ViewHolder {
TextView nome;
TextView bookType;
ImageView bookImage;
public BookViewHolder(#NonNull View itemView) {
super(itemView);
nome = itemView.findViewById(R.id.nome);
bookType = itemView.findViewById(R.id.email);
bookImage = itemView.findViewById(R.id.book_image);
itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
int position = getLayoutPosition();
return true;
}
});
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String nomeDelete = nome.getText().toString();
int position = getLayoutPosition();
FragmentManager fm = BookAdapter.supportFragment;
BookDialogFragment bookDialogFragment =
BookDialogFragment.newInstance("Atenção:", nomeDelete, position);
bookDialogFragment.show(fm, "fragment_alert");
}
});
}
}
I've seen other similar solutions, but I didn't get to implement them in my code. Can anyone help me?
Going through your code, it doesn't seem like you're using Navigation Component. So let's do it this way.
Your BookDialogFragment is a child fragment to BookFragment. So basically, you should set a result in your dialog when remove button is clicked. Then set up a result listener code in you parent fragment (BookFragment), so as to get immediate result to act upon.
First part, set a result when remove is clicked
NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
...
if (Objects.equals(MESSAGE_TO_DIALOG_FRAGMENT, "Book removed!")) {
...
} else {
alertDialogBuilder.setPositiveButton("Remove", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// here I want to callback my fragment
BookFragment deleteBook = new BookFragment();
deleteBook.removeBook(nameToDelete, position);
Bundle result = new Bundle();
result.putBoolean("value", true);
getParentFragmentManager().setFragmentResult("removed", result);
}
});
alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
...
}
}
}
Then, listen for and act on the result in you BookFragment, wherever you want to, with these lines.
getChildFragmentManager().setFragmentResultListener("removed", this, new FragmentResultListener() {
#Override
public void onFragmentResult(#NonNull String requestKey, #NonNull Bundle bundle) {
// We use a Boolean here, but any type that can be put in a Bundle is supported
Boolean result = bundle.getString("value");
// Do something with the result
}
});
PS: You can use constant variables where you have "removed" and "value" for accuracy.
Hope this helps :)
I am trying to create a note-taking app focusing on color-changing.
In one of the activity, I am trying to implement item addition and deletion to the note
picture_of_the_activity.
Item addition obviously works as intended.
The problem is with the deletion, it is very inconsistent: when I delete all the item starting from the last one upward, everything work fine; if I delete items in another order the adapter mess up, do not always delete the correct one and crashes when removing the last item.
I looked it up and found some possible solution (here or in other websites) but couldn't find a solution, or, so I thought.
I created methods inside the adapter addItem() and removeItem() so any changes is done within the class.
Maybe I am misunderstanding a concept or missing something?
Thank you in advance!
some interfaces or protion of the code are missing because I didn't implement some features yet
Activity Code
package com.example.colornoteplus;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
public class CheckListNoteActivity extends AppCompatActivity{
// note editable views
private EditText titleView;
private TextView titleCharacterCount;
private ImageButton colorView;
private RecyclerView contentView;
private FloatingActionButton fab;
CheckListAdapter adapter;
// toolbar
private Toolbar toolbar;
// Current note
private CheckListNote note;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getNoteFromIntent();
note.getContent().add(new CheckListItem("ONE"));
note.getContent().add(new CheckListItem("TWO"));
note.getContent().add(new CheckListItem("THREE"));
note.getContent().add(new CheckListItem("FOUR"));
note.getContent().add(new CheckListItem("FIVE"));
changeViewsColor(0);
}
// Method used to add menus and configure button action
// like OnClickListeners ...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Create a submenu for sorting purpose
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_check_list_activity,menu);
return super.onCreateOptionsMenu(menu);
}
// get note from the intent
private void getNoteFromIntent(){
if (!getIntent().getStringExtra(Statics.KEY_NOTE_ACTIVITY).equals(Statics.NOTE_DEFAULT_UID)){
note = MySharedPreferences.LoadCheckListNoteFromSharedPreferences(getIntent().getStringExtra(Statics.KEY_NOTE_ACTIVITY),getApplicationContext());
} else {
note = new CheckListNote();
}
}
private void changeViewsColor(int color){
// set the global theme
setTheme(StyleManager.getTheme(color));
// set the appropriate layout
setContentView(R.layout.activity_check_list_note);
// change status bar color
getWindow().setStatusBarColor(getResources().getColor(StyleManager.getThemeColor(color)));
// set up FAB action
fab = findViewById(R.id.fab_add_item);
fab.setOnClickListener(view -> onFabClickListener());
// setting the toolbar
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setBackgroundColor(getResources().getColor(StyleManager.getThemeColor(color)));
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
// setting the note title
titleView = findViewById(R.id.note_title_view);
titleView.setText("");
titleView.setTextColor(getResources().getColor(StyleManager.getThemeColorDark(color)));
titleView.setHintTextColor(getResources().getColor(StyleManager.getThemeColorLight(color)));
// setting the character counter for the note title
titleCharacterCount = findViewById(R.id.note_title_characters);
String m = titleView.getText().toString().trim().length()+ getString(R.string.text_divider)+ getResources().getInteger(R.integer.title_max_length);
titleCharacterCount.setText(m);
titleCharacterCount.setTextColor(getResources().getColor(StyleManager.getThemeColor(color)));
titleView.addTextChangedListener(new TextWatcher()
{
#Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int aft)
{
}
#Override
public void afterTextChanged(Editable s)
{
// this will show characters remaining
String msg = titleView.getText().toString().length()+ getString(R.string.text_divider)+ getResources().getInteger(R.integer.title_max_length);
titleCharacterCount.setText(msg);
}
});
// setting the color view
colorView = findViewById(R.id.note_color_view);
colorView.setOnClickListener(view -> buildColorPickDialog());
colorView.setBackgroundResource(StyleManager.getBackground(color));
adapter = new CheckListAdapter(getApplicationContext(),note.getContent(),color);
adapter.setOnItemClickListener(new CheckListAdapter.OnItemClickListener() {
#Override
public void onChecked(int position) {
Toast.makeText(CheckListNoteActivity.this, "Checked item: " +position, Toast.LENGTH_SHORT).show();
}
#Override
public void onUnchecked(int position) {
Toast.makeText(CheckListNoteActivity.this, "Unchecked item: " +position, Toast.LENGTH_SHORT).show();
}
#Override
public void onSetPriority(int position) {
}
#Override
public void onSetReminder(int position) {
}
#Override
public void onDelete(int position) {
adapter.removeItem(position);
Toast.makeText(CheckListNoteActivity.this, "List has "+note.getContent().size()+" elements", Toast.LENGTH_SHORT).show();
}
});
contentView = findViewById(R.id.note_content_view);
contentView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
contentView.setAdapter(adapter);
}
private void switchColor(int color){
String tempTitle = titleView.getText().toString().trim();
changeViewsColor(color);
titleView.setText(tempTitle);
}
// build the color picker dialog
private void buildColorPickDialog(){
FragmentPickColor fragment = new FragmentPickColor(new ColorAdapter(),5,note.getColor());
fragment.show(getSupportFragmentManager(),Statics.TAG_FRAGMENT_COLOR_PICK);
fragment.setOnItemClickListener(new ColorAdapter.OnItemClickListener() {
#Override
public void OnClickListener(int position) {
note.setColor(position);
switchColor(position);
fragment.dismiss();
}
#Override
public void OnLongClickListener(int position) {
}
});
}
private void onFabClickListener(){
FragmentAddCheckListItem fragment = new FragmentAddCheckListItem(note.getColor());
fragment.show(getSupportFragmentManager(),Statics.TAG_FRAGMENT_ADD_CHECK_LIST_ITEM);
fragment.setOnClickListener(new FragmentAddCheckListItem.OnClickListener() {
#Override
public void onConfirmClickListener() {
fragment.getItem().setDescription(fragment.getInputText());
adapter.addItem(fragment.getItem(),0);
fragment.dismiss();
}
#Override
public void onSetPriorityClickListener() {
}
#Override
public void onSetDueTimeClickListener() {
FragmentDatePicker datePicker = new FragmentDatePicker();
datePicker.show(getSupportFragmentManager(),Statics.TAG_FRAGMENT_DATE_PICKER);
datePicker.setOnDateSet((year, month, day) -> {
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR,year);
c.set(Calendar.MONTH,month);
c.set(Calendar.DAY_OF_MONTH,day);
fragment.getItem().setDueDate(c.getTime().getTime());
fragment.setDueTimeText(DateFormat.getDateInstance().format(new Date(fragment.getItem().getDueDate())));
});
}
});
}
}
Adapter
package com.example.colornoteplus;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
public class CheckListAdapter extends RecyclerView.Adapter<CheckListAdapter.MyViewHolder> {
public CheckListAdapter(Context context,ArrayList<CheckListItem> list, int color) {
this.list = list;
this.color = color;
this.context = context;
}
final private ArrayList<CheckListItem> list;
final private int color;
final private Context context;
private OnItemClickListener listener;
#NonNull
#Override
public CheckListAdapter.MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new CheckListAdapter.MyViewHolder(LayoutInflater.
from(parent.getContext()).
inflate(R.layout.item_check_list,parent,false));
}
#Override
public void onBindViewHolder(#NonNull CheckListAdapter.MyViewHolder holder, int position) {
CheckListItem currentItem = list.get(position);
holder.background.setBackgroundResource(StyleManager.getBackgroundLight(color));
holder.checkBox.setOnCheckedChangeListener((compoundButton, b) -> {
if (b) listener.onChecked(position);
else listener.onUnchecked(position);
});
holder.title.setTextColor(context.getResources().getColor(StyleManager.getThemeColorDark(color)));
holder.title.setHintTextColor(context.getResources().getColor(StyleManager.getThemeColor(color)));
assert currentItem != null;
holder.title.setText(currentItem.getDescription().trim());
holder.dueTimeText.setTextColor(context.getResources().getColor(StyleManager.getThemeColor(color)));
holder.dueTimeText.setText(currentItem.getDoneDate() != -1 ? DateFormat.getDateInstance().format(new Date(currentItem.getDueDate())) : context.getString(R.string.set_reminder));
holder.dueTimeText.setOnClickListener(view -> listener.onSetReminder(position));
holder.priorityText.setTextColor(context.getResources().getColor(StyleManager.getThemeColor(color)));
holder.priorityText.setText(currentItem.priorityToString(context));
holder.priorityText.setOnClickListener(view -> listener.onSetPriority(position));
holder.delete.setBackgroundResource(StyleManager.getBackground(color));
holder.delete.setOnClickListener(view -> {
// listener.onDelete(position);
removeItem(position);
});
}
#Override
public int getItemCount() {
return list.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder{
CheckBox checkBox;
ConstraintLayout background;
EditText title;
ImageButton delete;
TextView priorityText,dueTimeText;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
checkBox = itemView.findViewById(R.id.item_check_box);
title = itemView.findViewById(R.id.item_title);
dueTimeText = itemView.findViewById(R.id.item_due_time_text);
priorityText = itemView.findViewById(R.id.item_priority_text);
delete = itemView.findViewById(R.id.item_delete);
background = itemView.findViewById(R.id.item_background);
}
}
public void addItem(CheckListItem item,int position){
if (position < 0){
list.add(item);
notifyItemInserted(list.size()-1);
}
else {
list.add(position,item);
notifyItemInserted(position);
}
}
public void removeItem(int position){
list.remove(position);
notifyItemRemoved(position);
}
public void setOnItemClickListener(OnItemClickListener listener){
this.listener = listener;
}
public interface OnItemClickListener{
void onChecked(int position);
void onUnchecked(int position);
void onSetPriority(int position);
void onSetReminder(int position);
void onDelete(int position);
}
}
public void addItem(CheckListItem item, int position) {
if (position >= 0) {
list.add(position, item);
notifyItemInserted(position);
}
}
public void removeItem(int position) {
if (position >= 0) {
list.remove(position);
notifyItemRemoved(position);
}
}
And in onBindViewHolder use holder.getAdapterPosition() instead of position in your listeners like this:
holder.delete.setOnClickListener(view -> {
// listener.onDelete(position);
removeItem(holder.getAdapterPosition());
});
I am developing a sharing app similar to Shareit, Xender. I want to show all the thumbnails of videos in a simple gridview but loading thumbnails takes a lot of time especially if I have more than a 1000 - 2000 videos.
So, I made a program to load each thumbnail on a new thread if it hasn't been loaded and then notify the base adapter.
The Code is :-
package com.*.filetransfer.Server.File.Selection.SubFragments;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.*.filetransfer.R;
import com.*.filetransfer.Strings;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.*.filetransfer.Server.File.Selection.FileSelection.selected_item_counter_down;
import static com.*.filetransfer.Server.File.Selection.FileSelection.selected_item_counter_up;
import static com.*.filetransfer.Server.File.Selection.FileSelection.videoList;
public class VideoGalleryFragment extends Fragment {
GridView gridView;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.gridview, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
videoList = new ArrayList<Video>();
gridView = view.findViewById(R.id.gridview);
String[] projection = new String[] {
MediaStore.Video.Media._ID,
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.DURATION,
MediaStore.Video.Media.SIZE
};
String selection = MediaStore.Video.Media.DURATION +
" >= ?";
String[] selectionArgs = new String[] {
String.valueOf(TimeUnit.MILLISECONDS.convert(5, TimeUnit.MILLISECONDS))};
String sortOrder = MediaStore.Video.Media.DATE_ADDED + " DESC";
try (Cursor cursor = getContext().getApplicationContext().getContentResolver().query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
selectionArgs,
sortOrder
)) {
// Cache column indices.
int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
int nameColumn =
cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME);
int durationColumn =
cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION);
int sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE);
while (cursor.moveToNext()) {
// Get values of columns for a given video.
long id = cursor.getLong(idColumn);
String name = cursor.getString(nameColumn);
int duration = cursor.getInt(durationColumn);
long size = cursor.getLong(sizeColumn);
Uri contentUri = ContentUris.withAppendedId(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id);
// Stores column values and the contentUri in a local object
// that represents the media file.
videoList.add(new Video(contentUri, name, duration, size, null, false, false));
}
gridView.setAdapter(new GalleryGridViewAdapter(requireContext(), videoList){
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ImageView imageView = (ImageView) convertView;
if (imageView == null){
imageView = new ImageView(requireContext());
imageView.setLayoutParams(new GridView.LayoutParams(300,300));
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setPadding(20,20,20,20);
}
if (( (Video) getItem(position)).isChecked()){
imageView.setBackgroundColor(getResources().getColor(R.color.aqua));
}
else{
imageView.setBackgroundColor(Color.TRANSPARENT);
}
if (((Video)getItem(position)).getThumbnail() == null && (!((Video)getItem(position)).isImageLoading())){
((Video)getItem(position)).setImageLoading(true);
new Thread(new Runnable() {
#Override
public void run() {
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor1 = requireContext().getContentResolver().query(((Video)getItem(position)).getUri(), filePathColumn, null, null, null);
cursor1.moveToFirst();
int columnIndex = cursor1.getColumnIndex(filePathColumn[0]);
String picturePath = cursor1.getString(columnIndex);
cursor1.close();
Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(picturePath, MediaStore.Video.Thumbnails.MINI_KIND);
((Video)getItem(position)).setThumbnail(bitmap);
((Video)getItem(position)).setImageLoading(true);
try {
requireActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
}
catch (IllegalStateException ignored){
}
}
}).start();
}
if (((Video)getItem(position)).getThumbnail() != null)
imageView.setImageBitmap(((Video)getItem(position)).getThumbnail());
return imageView;
}
});
}
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (!videoList.get(position).isChecked()) {
videoList.get(position).setChecked(true);
selected_item_counter_up();
Log.e(Strings.TAG, String.valueOf(videoList.get(position).getSize()));
try {
((ImageView) view).setBackgroundColor(getResources().getColor(R.color.aqua));
} catch (Exception e) {
}
}
else{
videoList.get(position).setChecked(false);
selected_item_counter_down();
try {
((ImageView) view).setBackgroundColor(Color.TRANSPARENT);
} catch (Exception e) {
}
}
}
});
}
}
class GalleryGridViewAdapter extends BaseAdapter {
private List<Video> videos;
private Context context;
public GalleryGridViewAdapter(Context context, List<Video> videoList) {
this.context = context;
this.videos = videoList;
}
#Override
public int getCount() {
return videos.size();
}
#Override
public Object getItem(int position) {
return videos.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
The static methods and variable imported on top are :-
public static List<Video> videoList;
public static void selected_item_counter_up(){
selected_counter++;
view_counter.setText("Selected (" + selected_counter + ")");
}
public static void selected_item_counter_down(){
selected_counter--;
view_counter.setText("Selected (" + selected_counter + ")");
}
My video class is simple as follows:-
package com.*.filetransfer.Server.File.Selection.SubFragments;
import android.graphics.Bitmap;
import android.net.Uri;
public class Video {
private final Uri uri;
private final String name;
private final int duration;
private final long size;
private Bitmap thumbnail;
private boolean isImageLoading;
private boolean isChecked;
public Video(Uri uri, String name, int duration, long size, Bitmap bitmap, boolean isImageLoading, boolean isChecked) {
this.uri = uri;
this.name = name;
this.duration = duration;
this.size = size;
this.thumbnail = bitmap;
this.isImageLoading = isImageLoading;
this.isChecked = isChecked;
}
public boolean isChecked() {
return isChecked;
}
public boolean isImageLoading() {
return isImageLoading;
}
public Uri getUri() {
return uri;
}
public String getName() {
return name;
}
public int getDuration() {
return duration;
}
public long getSize() {
return size;
}
public Bitmap getThumbnail() {
return thumbnail;
}
public void setThumbnail(Bitmap thumbnail) {
this.thumbnail = thumbnail;
}
public void setImageLoading(boolean imageLoading) {
isImageLoading = imageLoading;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
}
The problem I am facing is, if the user scrolls too fast mostly all of the thumbnails are mixed up and some load multiple times. Can someone spot the flaw and correct it.
If you can please suggest an alternative way to load and show thumbnails of media on device, if possible seperated by date as I am unable to figure out how to do it
EDIT
I switched to recyclerview and it scrolls very smoothly but It is scrolling to top for some reason. Here is the updated code:-
package com.*.filetransfer.Server.File.Selection.SubFragments;
import android.graphics.Color;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.sugarsnooper.filetransfer.R;
import com.*.filetransfer.Server.File.Selection.Media;
import com.*.filetransfer.Server.Send_Activity;
import java.io.File;
import java.util.ArrayList;
import java.util.Map;
import static com.*.filetransfer.Server.File.Selection.FileSelection.imageList;
public class Photos extends Fragment {
RecyclerView gridView;
GridAdapter ga = new GridAdapter();
private int max_padding = 20;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.gridview, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
new Thread(new Runnable() {
#Override
public void run() {
imageList = new ArrayList<>();
for (Map.Entry<String, Long> entry : Send_Activity.readableRoots.getImages().entrySet())
{
File file = new File(entry.getKey());
imageList.add(new Media(Uri.fromFile(file), file.getName(), file.length(), entry.getValue()));
}
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
gridView = view.findViewById(R.id.gridview);
gridView.setLayoutManager(new GridLayoutManager(requireContext(), 3));
gridView.setAdapter(ga);
gridView.addItemDecoration(new SpacesItemDecoration(30));
}
});
}
}).start();
}
class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int space;
public SpacesItemDecoration(int space) {
this.space = space;
}
#Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
outRect.left = space;
outRect.right = space;
outRect.bottom = space;
// Add top margin only for the first item to avoid double space between items
if (parent.getChildLayoutPosition(view) == 0) {
outRect.top = space;
} else {
outRect.top = 0;
}
}
}
class GridAdapter extends RecyclerView.Adapter<GridAdapter.MyViewHolder> {
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View layout = LayoutInflater.from(requireActivity()).inflate(R.layout.grid_item, parent, false);
return new MyViewHolder(layout);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int i) {
if (((Media) getItem(i)).isChecked()) {
holder.ivparent.setPadding(max_padding, max_padding, max_padding, max_padding);
holder.ivparent.setBackgroundColor(Color.parseColor("#777777"));
}
else{
holder.ivparent.setPadding(0, 0, 0, 0);
holder.ivparent.setBackgroundColor(Color.parseColor("#00777777"));
}
Glide.with(requireActivity()).fromUri().dontAnimate().load(((Media) getItem(i)).getUri()).into(holder.imageView);
}
#Override
public int getItemCount() {
return imageList.size();
}
#Override
public void onViewRecycled(#NonNull MyViewHolder holder) {
super.onViewRecycled(holder);
holder.recycle();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
LinearLayout ivparent;
ImageView imageView;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
imageView = (ImageView) itemView.findViewById(R.id.imageView);
ivparent = itemView.findViewById(R.id.iv_parent);
}
public void recycle() {
Glide.clear(imageView);
}
}
public Object getItem(int i) {
return imageList.get(i);
}
}
}
To achieve the same behavior I used Recyclerview + GridLayoutManager.
And loaded thumbnail from video uris using Glide inside onBindViewHolder.
So thumbnail for only items that are currently loaded in the screen gets created, and it handles async loading and clearing the views
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
// Code for loading thumbnail using video file path
Glide.with(context)
.load(Uri.fromFile(new File(videoFilePath)))
.thumbnail(0.5f)
.into(imageView);
}
Hello i have this error:
Error:(36, 19) error: constructor DrawerItem in class DrawerItem cannot be applied to given types;
required: int,int,Class<? extends Fragment>
found: String,int,Class<TODOFragment_>
reason: actual argument String cannot be converted to int by method invocation conversion
Note: C:\Users\Kisiel\AndroidStudioProjects\Studentizer\app\src\main\java\pl\edu\ug\aib\studentizerApp\fragment\TimetableFragment.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Error:Execution failed for task ':app:compileDebugJava'.
> Compilation failed; see the compiler error output for details.
Information:BUILD FAILED
This is my code TODOFragment:
package pl.edu.ug.aib.studentizerApp.fragment;
import android.annotation.TargetApi;
import android.app.Fragment;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.Toast;
import org.androidannotations.annotations.EFragment;
import java.util.ArrayList;
import java.util.List;
import pl.edu.ug.aib.studentizerApp.R;
import pl.edu.ug.aib.studentizerApp.todoList.DatabaseHandler;
import pl.edu.ug.aib.studentizerApp.todoList.Task;
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#EFragment(R.layout.fragment_todo)
public class TODOFragment extends Fragment {
EditText zadanieTxt, opisTxt, dataTxt, adresTxt;
List<Task> Tasks = new ArrayList<Task>();
ListView TaskListView;
DatabaseHandler dbHandler;
#Override
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
return inflater.inflate(R.layout.fragment_todo, container, false);
}
#Override
public void onStart() {
super.onStart();
zadanieTxt = (EditText) getView().findViewById(R.id.txtZadanie);
opisTxt = (EditText) getView().findViewById(R.id.txtOpis);
dataTxt = (EditText) getView().findViewById(R.id.txtData);
adresTxt = (EditText) getView().findViewById(R.id.txtAdres);
TaskListView = (ListView) getView().findViewById(R.id.listView);
dbHandler = new DatabaseHandler(getActivity().getApplicationContext());
TabHost tabHost = (TabHost) getView().findViewById(R.id.baner);
tabHost.setup();
TabHost.TabSpec tabSpec = tabHost.newTabSpec("Dodaj zadanie");
tabSpec.setContent(R.id.tabZadanie);
tabSpec.setIndicator("Dodaj Zadanie");
tabHost.addTab(tabSpec);
tabSpec = tabHost.newTabSpec("lista");
tabSpec.setContent(R.id.tabListaZadan);
tabSpec.setIndicator("Lista");
tabHost.addTab(tabSpec);
final Button addBtn = (Button) getView().findViewById(R.id.btnAdd);
addBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Task task = new Task(dbHandler.getTaskCount(), String.valueOf(zadanieTxt.getText()), String.valueOf(opisTxt.getText()), String.valueOf(dataTxt.getText()), String.valueOf(adresTxt.getText()));
if (!taskExists(task)) {
dbHandler.createZadanie(task);
Tasks.add(task);
Toast.makeText(getActivity().getApplicationContext(), String.valueOf(zadanieTxt.getText()) + " zostało dodane do listy zadań!", Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(getActivity().getApplicationContext(), String.valueOf(zadanieTxt.getText()) + " Zadanie już istnieje", Toast.LENGTH_SHORT).show();
}
});
zadanieTxt.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
addBtn.setEnabled(String.valueOf(zadanieTxt.getText()).trim().length() > 0);
}
#Override
public void afterTextChanged(Editable editable) {
}
});
if (dbHandler.getTaskCount() != 0)
Tasks.addAll(dbHandler.getAllTasks());
populateList();
}
private boolean taskExists(Task task) {
String zadanie = task.getZadanie();
int taskCount = Tasks.size();
for (int i = 0; i < taskCount; i++) {
if (zadanie.compareToIgnoreCase(Tasks.get(i).getZadanie()) == 0)
return true;
}
return false;
}
private void populateList() {
ArrayAdapter<Task> adapter = new TaskListAdapter();
TaskListView.setAdapter(adapter);
}
private class TaskListAdapter extends ArrayAdapter<Task> {
public TaskListAdapter()
{
super (TODOFragment.this.getActivity(), R.layout.listview_item, Tasks);
}
#Override
public View getView(int position, View view, ViewGroup parent) {
if (view == null)
view = getActivity().getLayoutInflater().inflate(R.layout.listview_item, parent, false);
Task currentTask = Tasks.get(position);
TextView zadanie = (TextView) view.findViewById(R.id.zadanie);
zadanie.setText(currentTask.getZadanie());
TextView opis = (TextView) view.findViewById(R.id.opis);
opis.setText(currentTask.getOpis());
TextView data = (TextView) view.findViewById(R.id.data);
data.setText(currentTask.getData());
TextView adres = (TextView) view.findViewById(R.id.adres);
adres.setText(currentTask.getAdres());
return view;
}
}
}
And DrawerListAdapter
package pl.edu.ug.aib.studentizerApp.navigationDrawer;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import org.androidannotations.annotations.AfterInject;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import java.util.ArrayList;
import java.util.List;
import pl.edu.ug.aib.studentizerApp.R;
import pl.edu.ug.aib.studentizerApp.fragment.TODOFragment_;
import pl.edu.ug.aib.studentizerApp.fragment.TimetableFragment_;
import pl.edu.ug.aib.studentizerApp.fragment.DashboardFragment_;
import pl.edu.ug.aib.studentizerApp.fragment.WalletFragment_;
#EBean
public class DrawerListAdapter extends BaseAdapter {
#RootContext
Context context;
List<DrawerItem> items = new ArrayList<DrawerItem>();
#AfterInject
void init() {
items.clear();
//Dodawanie do Drawera listy fragmentów
items.add(new DrawerItem(R.string.title_dashboard, R.drawable.icon_home, DashboardFragment_.class));
items.add(new DrawerItem(R.string.title_timetable, R.drawable.ic_directions_transit_white_24dp, TimetableFragment_.class));
items.add(new DrawerItem("Lista zadań", R.drawable.icon_about, TODOFragment_.class));
items.add(new DrawerItem(R.string.title_wallet, R.drawable.wallet_grey, WalletFragment_.class));
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
DrawerItemView drawerItemView;
if (convertView == null) {
drawerItemView = DrawerItemView_.build(context);
} else {
drawerItemView = (DrawerItemView) convertView;
}
drawerItemView.bind(getItem(position));
return drawerItemView;
}
#Override
public int getCount() {
return items.size();
}
#Override
public DrawerItem getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
}
I Already I searched many topics on the forum but did not find answers how I can fix it. I hope that some of you help me. If I have put some pieces of code, write it in the comments.
package pl.edu.ug.aib.studentizerApp.navigationDrawer;
import android.support.v4.app.Fragment;
public class DrawerItem {
private int titleResId;
private int iconResId;
private Class<? extends Fragment> fragmentClass;
public DrawerItem(int titleResId, int iconResId, Class<? extends Fragment> fragmentClass) {
this.titleResId = titleResId;
this.iconResId = iconResId;
this.fragmentClass = fragmentClass;
}
public int getTitleResId() {
return titleResId;
}
public int getIconResId() {
return iconResId;
}
public Class<? extends Fragment> getFragmentClass() {
return fragmentClass;
}
}
The error is in here
items.add(new DrawerItem("Lista zadań", R.drawable.icon_about, TODOFragment_.class));
You are passing an String as parameters and it should be and int. Put "Lista zadań" in the resources and pass it as the other strings
This line is incorrect, the method DrawerItem() must receive an int value.
items.add(new DrawerItem("Lista zadań", R.drawable.icon_about, TODOFragment_.class)); //ERROR! :P
so add into your Strings.xml file:
<string name="title_lista">Lista zadań</string>
then change the third line to:
items.add(new DrawerItem(R.string.title_lista, R.drawable.icon_about, TODOFragment_.class)); //ERROR! :P