I'm writing a memo keeper app. It has a ViewPager that allows you to swipe between categories of memos. When I add a memo in a page, the memo is listed in the ListView of the fragment.
But if i swipe to another page and swipe back, then add another memo, that memo is not listed in the ListView. I tried notifyDatasetChanged() of the adapter and invalidateViews() of the ListView but nothing seem to work.
I don't know, where I made a mistake. I hope if you can find what I did wrong, it will very helpful for me.
Here is my Fragment code:
public class MemoListFragment extends Fragment
{
private MemoAdapter memoAdapter = null;
private ListView memoListView;
private Memo memoToEdit = null;
private List<Memo> userSelected = new ArrayList<>();
private onDeleteActionSelectedListener
deleteActionSelectedListener;
private String category;
private List<Memo> memoList;
public String getCategory()
{
return category;
}
public void setCategory(String category)
{
this.category = category;
}
private final int EDIT_MEMO_REQUEST_CODE = 2;
public void updateMemoList(List<Memo> memoList)
{
memoAdapter.clear();
memoAdapter.addAll(memoList);
}
public void setInitData(List<Memo> memoList)
{
this.memoList = memoList;
}
public void addMemo(Memo m)
{
memoAdapter.add(m);
memoListView.invalidateViews();
}
public MemoListFragment()
{
// Required empty public constructor
}
#Override
public void onAttach(Context context)
{
super.onAttach(context);
deleteActionSelectedListener = ((MainActivity)
context).getMainPresenter();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup
container,
Bundle savedInstanceState)
{
View rootLayout = inflater.inflate(R.layout.memo_list_fragment,
container, false);
memoListView = rootLayout.findViewById(R.id.memo_list_view);
memoListView.setOnItemClickListener((parent, view, position,
id) ->
{
{
Memo selectedMemo = memoAdapter.getItem(position);
Intent editMemo = new Intent(getActivity(),
MemoViewActivity.class);
editMemo.putExtra("memo_to_edit", selectedMemo);
memoToEdit = selectedMemo;
startActivityForResult(editMemo,
EDIT_MEMO_REQUEST_CODE);
}
});
memoAdapter = new MemoAdapter(getActivity(), 0, memoList);
memoListView.setAdapter(memoAdapter);
memoListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL);
memoListView.setMultiChoiceModeListener(modeListener);
return rootLayout;
}
#Override
public void onActivityResult(int requestCode, int resultCode,
Intent data)
{
if (requestCode == EDIT_MEMO_REQUEST_CODE)
{
if (resultCode == Activity.RESULT_OK)
{
String title = data.getStringExtra("title").toString();
String detail =
data.getStringExtra("detail").toString();
if (memoToEdit != null)
{
memoToEdit.setDetail(detail);
memoToEdit.setTitle(title);
memoToEdit.setLastModified(LocalDate.now());
memoToEdit = null;
}
}
}
}
AbsListView.MultiChoiceModeListener modeListener = new
AbsListView.MultiChoiceModeListener()
{
#Override
public void onItemCheckedStateChanged(ActionMode mode, int
position, long id, boolean checked)
{
Memo m = memoAdapter.getItem(position);
if (userSelected.contains(m))
{
userSelected.remove(m);
memoAdapter.getSelectedPos().remove(new
Integer(position));
}
else
{
userSelected.add(m);
memoAdapter.getSelectedPos().add(position);
}
memoListView.invalidateViews();
mode.setTitle(userSelected.size() + " items selected.");
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu)
{
mode.getMenuInflater().inflate(R.menu.context_menu, menu);
memoAdapter.setShowCheckboxes(true);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu)
{
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem
item)
{
switch (item.getItemId())
{
case R.id.action_delete:
for (Memo m : userSelected)
memoAdapter.remove(m);
deleteActionSelectedListener.onDeleteActionSelected(userSelected);
mode.finish();
return true;
default:
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode mode)
{
userSelected.clear();
memoAdapter.getSelectedPos().clear();
memoAdapter.setShowCheckboxes(false);
}
};
public interface onDeleteActionSelectedListener
{
void onDeleteActionSelected(List<Memo> userSelected);
}
}
Here is the code to add a memo:
public void addMemo(Memo memo)
{
int currentPage = mainView.getCurrentPagePosition();
if (currentPage == 0)//on page All category
memo.setCategory("Not categorized");
else
memo.setCategory(memoManager.getCategoriesList().get(currentPage));
MemoListFragment currentFrag = fragmentList.get(currentPage);
currentFrag.addMemo(memo);
}
Here is my PagerAdapter code:
public class MemooPagerAdapter extends FragmentStatePagerAdapter
{
private FragmentSetupManager fragmentSetupManager;
private List<String> categoriesList;
public MemooPagerAdapter(FragmentManager fm, FragmentSetupManager fragmentSetupManager)
{
super(fm);
this.fragmentSetupManager = fragmentSetupManager;
this.categoriesList = fragmentSetupManager.getCategoriesList();
}
#Override
public Fragment getItem(int position)
{
MemoListFragment fragment = new MemoListFragment();
String cate = categoriesList.get(position);
fragment.setCategory(cate);
fragment.setInitData(fragmentSetupManager.getFragmentData(cate));
fragmentSetupManager.addToFragmentList(fragment);
return fragment;
}
#Override
public int getCount()
{
return categoriesList.size();
}
#Nullable
#Override
public CharSequence getPageTitle(int position)
{
return categoriesList.get(position);
}
public interface FragmentSetupManager
{
List<Memo> getFragmentData(String category);
void addToFragmentList(MemoListFragment fragment);
List<String> getCategoriesList();
}
}
ViewPager stetup:
public MainPresenter(MainView mainView)
{
this.mainView = mainView;
memoManager = new MemoManager(((Activity) mainView).getApplicationContext());
pagerAdapter = new MemooPagerAdapter(
((FragmentActivity) mainView).getSupportFragmentManager(), this);
mainView.setMainVPagerAdapter(pagerAdapter);
}
Ideally, you want to communicate an event from one fragment to another. The easiest of ways is to use RXJava. This approach is the best way of doing that and easy to implement. In my opinion, this is the best approach for communicating two or maore fragments in Android.
Set up your project with the dependencies:
// RxJava2 Dependencies
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
So if you are in FragmentA and move to FragmentB, and made a change in FragmentB, FragmentA will get this change.
See how this was implemented easily here. Do let me know if it is sufficient.
Related
I am using a recyclerView to show a grid of movie posters. The posters are contained in a List<> along with their respective title and so on.
I implemented a searchView widget and I can successuflly get a List of matching results. But I can't hide the other ones.
As you understand I don't want to delete the irrelevant movies from the adapter or the user would not be able to see them again.
This is the code:
public class SearchUtils {
public static List<String> search(List<Show> list, String keyword){
List<String> results = new ArrayList<>();
for (Show curVal : list){
String curTitle = curVal.getTitle().toLowerCase().trim();
if (curTitle.contains(keyword)){
results.add(curTitle);
}else{
results = new ArrayList<>();
}
}
return results;
}
}
ListFragment.java
public class ListFragment extends Fragment implements LoaderManager.LoaderCallbacks<List<Show>> {
private static final String LOG_TAG = "ListFragment";
private static final String ARG_SCOPE = "com.dcs.shows.activity_to_launch";
private static final String BASE_URL = "http://api.themoviedb.org/3";
private TextView tv;
private ProgressBar pb;
private int scope;
private RecyclerView mRecyclerView;
private ShowAdapter mShowAdapter;
private SearchView mSearchView;
public static ListFragment newInstance(int target) {
Bundle args = new Bundle();
args.putInt(ARG_SCOPE, target);
ListFragment fragment = new ListFragment();
fragment.setArguments(args);
return fragment;
}
public ListFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
scope = getArguments().getInt(ARG_SCOPE);
setHasOptionsMenu(true);
Log.i(LOG_TAG, "onCreate#Scope is: " + scope);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_list, container, false);
mShowAdapter = new ShowAdapter(new ArrayList<Show>());
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
GridLayoutManager glm = new GridLayoutManager(getActivity(), 4);
mRecyclerView.setLayoutManager(glm);
mRecyclerView.addItemDecoration(new SpacesItemDecoration(8, getActivity()));
mRecyclerView.setAdapter(mShowAdapter);
mRecyclerView.addOnScrollListener(new EndlessRecyclerViewScrollListener(glm) {
#Override
public void onLoadMore(int page, int totalItemsCount) {
// Triggered only when new data needs to be appended to the list
// Add whatever code is needed to append new items to the bottom of the list
}
});
pb = (ProgressBar)rootView.findViewById(R.id.progress_view);
ConnectivityManager connMgr = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
// fetch data
getLoaderManager().initLoader(1, null, this);
} else {
// display error
pb.setVisibility(View.GONE);
}
return rootView;
}
List<Show> searchList;
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
getActivity().getMenuInflater().inflate(R.menu.main, menu);
final MenuItem myActionMenuItem = menu.findItem( R.id.action_search);
mSearchView = (SearchView) myActionMenuItem.getActionView();
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String s) {
if(!mSearchView.isIconified()) {
mSearchView.setIconified(true);
}
myActionMenuItem.collapseActionView();
return false;
}
#Override
public boolean onQueryTextChange(String s) {
if(s != null || !s.isEmpty()) {
for(Show movie : mShowAdapter.getList()) {
if(movie.getTitle().toLowerCase().contains(s.toLowerCase())){
mShowAdapter.add(movie);
}
mShowAdapter.notifyDataSetChanged();
}
} else {
mShowAdapter.addItemsToList(searchList, false);
}
return false;
}
});
mSearchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
#Override
public void onViewDetachedFromWindow(View arg0) {
// search was detached/closed
Log.v(LOG_TAG, "Restoring list: " + searchList + " size: " + searchList.size());
mShowAdapter.addItemsToList(searchList, false);
}
#Override
public void onViewAttachedToWindow(View arg0) {
// search was opened
searchList = mShowAdapter.getList();
}
});
}
private class ShowHolder extends RecyclerView.ViewHolder {
public ImageView mImageView;
public TextView mTextView;
public ShowHolder(View itemView) {
super(itemView);
mImageView = (ImageView) itemView.findViewById(R.id.grid_item_image);
mTextView = (TextView) itemView.findViewById(R.id.grid_item_title);
}
}
private class ShowAdapter extends RecyclerView.Adapter<ShowHolder> {
private List<Show> mShows;
public ShowAdapter(List<Show> shows) {
mShows = shows;
}
public void add(Show show){
mShows.add(show);
notifyDataSetChanged();
}
public void addItemsToList(List<Show> newShows, boolean append){
if(append){
mShows.addAll(newShows);
}else {
mShows = newShows;
}
notifyDataSetChanged();
}
public void removeItemsFromList(int index){
mShows.remove(index);
notifyItemRemoved(index);
}
public List<Show> getList(){
return mShows;
}
#Override
public ShowHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(getActivity());
View rootView = inflater.inflate(R.layout.list_item_row, parent, false);
return new ShowHolder(rootView);
}
#Override
public void onBindViewHolder(ShowHolder holder, int position) {
Show currentShow = mShows.get(position);
holder.mTextView.setText(currentShow.getTitle());
Glide.with(getActivity()).load(currentShow.getImage()).into(holder.mImageView);
}
#Override
public int getItemCount() {
return mShows.size();
}
}
#Override
public Loader<List<Show>> onCreateLoader(int i, Bundle bundle) {
//start the loader with the appropriate uri
//for now it only supports movies+popular
//it will support movies+top, tv+popular, tv+top.
Uri baseUri = Uri.parse(BASE_URL);
Uri.Builder uriBuilder = baseUri.buildUpon();
uriBuilder.appendPath("movie");
uriBuilder.appendPath("popular");
uriBuilder.appendQueryParameter("api_key", QueryUtils.API_KEY);
uriBuilder.appendQueryParameter("page", Integer.valueOf(1).toString());
Log.v(LOG_TAG, "onCreateLoader#URL built: " + uriBuilder.toString());
return new ShowLoader(getActivity(), uriBuilder.toString());
}
#Override
public void onLoadFinished(Loader<List<Show>> loader, List<Show> shows) {
// Clear the adapter of previous earthquake data
clearAdapter();
// If there is a valid list of Shows, then add them to the adapter's
// data set. This will trigger the ListView to update.
if (shows != null && !shows.isEmpty()) {
mShowAdapter.addItemsToList(shows, false);
mShowAdapter.notifyDataSetChanged();
}
pb.setVisibility(View.GONE);
}
#Override
public void onLoaderReset(Loader<List<Show>> loader) {
// Loader reset, so we can clear out our existing data.
clearAdapter();
}
private void clearAdapter(){
List<Show> empty = new ArrayList<>();
mShowAdapter.addItemsToList(empty, false);
mShowAdapter.notifyDataSetChanged();
}
Thanks
You can use two lists, one with all the elements (original), and one with just queried elements (this one should use recyclerview adapter). When querying, just select from original list and add them to adapter list, then notify changes. Don't forget to clear adapter list before adding new entries.
Edit: you can try something like this on onQueryTextChange method. Adapt for your own wish.
if(s != null && !s.isEmpty()) {
for(String movie : originalList) {
if(movie.toLowerCase().contains(s.toLowerCase()){
adapter.add(movie);
}
notifyChanges();
}
}
} else { adapter.addAll(originalList); }
I'm facing a few problems for a while now which I'm having trouble to solve. So I refer to the Realm and RecyclerView geniuses among the community.
I'm woking on a ToDo-List that sets completed tasks back to the ToDo-List after 2 days. The app uses a ViewPager with two tabs: "TODO" & "DONE".
1. RecyclerView
1.1. I want the completed Tasks from fragment 1 to be sent back to fragment 0 automatically after 2 days.
The Problem: If the counter is at 0 (or below) the item gets sent to fragment 0.
If I delte the item in the next line I get an exception error: "java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling"
So I put the delete function into a handler. Then it's working BUT only if ONE gets sent back. If many items get sent back simultaneously the app crashes. When I reopen the app everything is working because it was successfully saved in realm but one item is always saved twice.
Where's the Problem (in DoneAdapter.java)?
2. Realm
2.1. When I add an Item to the RecyclerView (and simultaneously to Realm), the item gets added at the bottom. But I want to add every new item at position 0.
(I know how to achieve this wih an ArrayList, but I want the items to be stored and displayed when I reopen the app, so I'm using Realm DB.)
Do you have any suggestions to achieve this?
2.2. Is it possible to implement later on the onLongClickListener for dragging and droping items and rearranging the position with Realm?
(I want to use this https://www.youtube.com/watch?v=tNgevYpyA9E)
2.3. I want to add some nice animations when I add and check an item. Realm doesn't support mRecyclerView.setItemAnimator(...); but I heard it is possible by adding mAdapter.setHasStableIds(true);. Unfortunately it throws an Exception: java.lang.IllegalStateException: Cannot change whether this adapter has stable IDs while the adapter has registered observers. (You can see this in my code below)
Do you have any solutions for that?
(optionally 1.4. Can you recommend me any Online DBs (e.g. Firebase) which I can sync with Realm or more generally: is it possible to sync an Online DB with Realm? Do you know any Tutorials (Udemy, YouTube) for setting up this sync process?)
Lastly: I want to update the Database with a background service every Midnight, so the counter in the completed section updates automatically. Does anyone know how to do this as well? Maybe with protected void onHandleIntent(Intent intent)?
Do you also know if there's an option in debugging mode to simulate passing time?
Here is the code:
MainActivity.java
public class MainActivity extends AppCompatActivity implements ToOtherFragmentCommunicator {
private ViewPagerAdapter mViewPagerAdapter;
private ViewPager mViewPager;
private static final int DONE = 1;
private static final int TODO = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mViewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mViewPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
RealmConfiguration configuration = new RealmConfiguration.Builder(this).build();
Realm.setDefaultConfiguration(configuration);
}
#Override
public void itemToOtherFragment(String data, int fragment) {
if (DONE == fragment) {
Done done = (Done) mViewPagerAdapter.getItem(fragment);
done.createDoneItem(data);
} else if (TODO == fragment) {
ToDo toDo = (ToDo) mViewPagerAdapter.getItem(fragment);
toDo.createToDoItem(data);
}
}
}
ToDo.java
public class ToDo extends Fragment {
private RecyclerView mRecyclerView;
private ToDoAdapter mAdapter;
private EditText taskInput;
private String taskName;
private Realm mRealm;
private RealmResults<ListItems> mResults;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View toDoView = inflater.inflate(R.layout.todo_layout, container, false);
mRecyclerView = (RecyclerView) toDoView.findViewById(R.id.todo_rv);
mRealm = Realm.getDefaultInstance();
mResults = mRealm.where(ListItems.class).equalTo("fragment", 0).findAllAsync();
setRecyclerView();
mRecyclerView.setItemAnimator(null);
//TODO add product to shopping list
final Handler handler = new Handler();
taskInput = (EditText) toDoView.findViewById(R.id.task_input);
taskInput.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (taskInput.getText().length() > 0 && (event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER)) {
// Perform action on key press
taskName = taskInput.getText().toString();
//Problem 2.1
//Code for adding item at the top with mRealm?
mRealm.beginTransaction();
createToDoItem(taskName);
mRealm.commitTransaction();
// mRecyclerView.scrollToPosition(0);
taskInput.setText(null);
handler.postDelayed(new Runnable() {
#Override
public void run() {
taskInput.setFocusableInTouchMode(true);
taskInput.setFocusable(true);
taskInput.requestFocus();
}
}, 200);
return true;
} else if (taskInput.length() == 0 && (event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER)) {
taskInput.clearFocus();
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(taskInput.getWindowToken(), 0);
return true;
}
return false;
}
});
return toDoView;
}
//TODO creates the shopping list item in DB
public void createToDoItem(String taskName) {
ListItems item = mRealm.createObject(ListItems.class);
long now = System.currentTimeMillis();
item.setAddedTime(now);
item.setFragment(0);
item.setTaskName(taskName);
mRealm.copyToRealmOrUpdate(item);
}
public void setRecyclerView() {
mRecyclerView.setHasFixedSize(true);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new ToDoAdapter(getActivity(), mRealm, mResults);
mRecyclerView.setAdapter(mAdapter);
//Problem 2.3.
//Produces "java.lang.IllegalStateException: Cannot change whether this adapter has stable IDs while the adapter has registered observers."
// mAdapter.setHasStableIds(true);
}
private RealmChangeListener mChangeListener = new RealmChangeListener() {
#Override
public void onChange() {
mAdapter.updateItems(mResults);
}
};
#Override
public void onStart() {
super.onStart();
mResults.addChangeListener(mChangeListener);
}
#Override
public void onStop() {
super.onStop();
mResults.removeChangeListener(mChangeListener);
}
}
ToDoAdapter.java
public class ToDoAdapter extends RecyclerView.Adapter<ListItemsViewHolder> {
private Context mContext;
private Realm mRealm;
private RealmResults<ListItems> mResults;
private int focusedItem = 0;
ToOtherFragmentCommunicator comm;
ToDoAdapter(Context context, Realm realm, RealmResults<ListItems> mResults) {
this.mContext = context;
this.mRealm = realm;
updateItems(mResults);
}
public void updateItems(RealmResults<ListItems> mResults) {
this.mResults = mResults;
notifyDataSetChanged();
}
//Problem 2.3.
//needed for mAdapter.setHasStableIds(true); in ToDo.java
// #Override
// public long getItemId(int position) {
// if (position < mResults.size()) {
// return mResults.get(position).getAddedTime();
// } else {
// return RecyclerView.NO_ID;
// }
// }
#Override
public ListItemsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.todo_item, parent, false);
comm = (ToOtherFragmentCommunicator) mContext;
return new ListItemsViewHolder(v);
}
#Override
public void onBindViewHolder(final ListItemsViewHolder holder, final int position) {
final ListItems items = mResults.get(position);
holder.taskName.setText(items.getTaskName());
holder.itemView.setSelected(focusedItem == position);
holder.getLayoutPosition();
holder.itemCheckbox.setOnCheckedChangeListener(null);
holder.itemCheckbox.setChecked(items.isSelected());
holder.itemCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mRealm.beginTransaction();
items.setSelected(isChecked);
//send item to Done
comm.itemToOtherFragment(items.getTaskName(), 1);
removeItem(position);
mRealm.commitTransaction();
}
});
}
#Override
public int getItemCount() {
return (mResults != null ? mResults.size() : 0);
}
private void removeItem(int position) {
mResults.get(position).removeFromRealm();
notifyDataSetChanged();
}
}
Done.java
public class Done extends Fragment {
private RecyclerView mRecyclerView;
private DoneAdapter mAdapter;
private Calendar calendar = Calendar.getInstance();
private Date date = new Date();
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy");
private Realm mRealm;
private RealmResults<ListItems> mResults;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View doneView = inflater.inflate(R.layout.done_layout, container, false);
mRecyclerView = (RecyclerView) doneView.findViewById(R.id.done_rv);
mRealm = Realm.getDefaultInstance();
mResults = mRealm.where(ListItems.class).equalTo("fragment", 1).findAllAsync();
setRecyclerView();
mRecyclerView.setItemAnimator(null);
return doneView;
}
//TODO creates the fridge item in DB
public void createDoneItem(String taskName) {
TimeZone.getDefault();
ListItems item = mRealm.createObject(ListItems.class);
long now = System.currentTimeMillis();
item.setAddedTime(now);
item.setFragment(1);
item.setTaskName(taskName);
item.setInputDate(simpleDateFormat.format(calendar.getTime()));
calendar.add(Calendar.DATE, 2);
item.setRenewDate(simpleDateFormat.format(calendar.getTime()));
//reset time to current date after adding days
calendar.setTime(date);
item.getRenewDate();
mRealm.copyToRealmOrUpdate(item);
}
public void setRecyclerView() {
mRecyclerView.setHasFixedSize(true);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new DoneAdapter(getActivity(), mRealm, mResults, Done.this);
mRecyclerView.setAdapter(mAdapter);
}
private RealmChangeListener mChangeListener = new RealmChangeListener() {
#Override
public void onChange() {
mAdapter.updateItems(mResults);
}
};
#Override
public void onStart() {
super.onStart();
mResults.addChangeListener(mChangeListener);
}
#Override
public void onStop() {
super.onStop();
mResults.removeChangeListener(mChangeListener);
}
}
DoneAdapter.java
public class DoneAdapter extends RecyclerView.Adapter<ListItemsViewHolder> {
private Context mContext;
private Done done;
private Realm mRealm;
private RealmResults<ListItems> mResults;
private int focusedItem = 0;
protected ToOtherFragmentCommunicator comm;
DoneAdapter(Context context, Realm realm, RealmResults<ListItems> results, Done done) {
this.mContext = context;
this.mRealm = realm;
this.done = done;
updateItems(results);
}
public void updateItems(RealmResults<ListItems> mResults) {
this.mResults = mResults;
notifyDataSetChanged();
}
#Override
public ListItemsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.done_item, parent, false);
comm = (ToOtherFragmentCommunicator) mContext;
return new ListItemsViewHolder(v);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
#Override
public void onBindViewHolder(final ListItemsViewHolder holder, final int position) {
final ListItems items = mResults.get(position);
holder.taskName.setText(items.getTaskName());
try {
if (items.getRenewCounter() == 1) {
holder.renewCounter.setText(mContext.getString(R.string.show_days_till_renew, items.getRenewCounter(), mContext.getString(R.string.day)));
} else {
holder.renewCounter.setText(mContext.getString(R.string.show_days_till_renew, items.getRenewCounter(), mContext.getString(R.string.days)));
}
holder.renewCounter.setTextColor(ContextCompat.getColor(mContext, R.color.colorAccent));
if (items.getRenewCounter() <= 0) {
mRealm.beginTransaction();
//Problem 1.1.
//send item back to todo list
comm.itemToOtherFragment(items.getTaskName(), 0);
// Produces "java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling" if there is no Handler
Handler handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
mRealm.beginTransaction();
removeItem(position);
mRealm.commitTransaction();
}
};
handler.post(r);
mRealm.commitTransaction();
}
} catch (ParseException e) {
e.printStackTrace();
}
holder.itemView.setSelected(focusedItem == position);
holder.getLayoutPosition();
}
#Override
public int getItemCount() {
return (mResults != null ? mResults.size() : 0);
}
private void removeItem(int position) {
mResults.get(position).removeFromRealm();
notifyDataSetChanged();
}
}
ListItems.java
public class ListItems extends RealmObject {
public ListItems(long addedTime, String taskName, String inputDate, String renewDate, int fragment) {
this.addedTime = addedTime;
this.taskName = taskName;
this.inputDate = inputDate;
this.renewDate = renewDate;
this.fragment = fragment;
}
#PrimaryKey
private long addedTime;
private int fragment;
#Ignore
private long renewCounter;
private String taskName, inputDate, renewDate;
private boolean selected;
public ListItems() {
}
public long getAddedTime() {
return addedTime;
}
public void setAddedTime(long addedTime) {
this.addedTime = addedTime;
}
public int getFragment() {
return fragment;
}
public void setFragment(int fragment) {
this.fragment = fragment;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getInputDate() {
return inputDate;
}
public void setInputDate(String inputDate) {
this.inputDate = inputDate;
}
public String getRenewDate() {
return renewDate;
}
public void setRenewDate(String renewDate) {
this.renewDate = renewDate;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public long getRenewCounter() throws ParseException {
TimeZone.getDefault();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy");
Date todayDate = new Date();
Date exDate = dateFormat.parse(renewDate);
this.renewCounter = daysBetween(todayDate, exDate);
return renewCounter;
}
private static long daysBetween(Date startDate, Date endDate) {
Calendar sDate = getDatePart(startDate);
Calendar eDate = getDatePart(endDate);
long daysBetween = 0;
while (sDate.before(eDate)) {
sDate.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
while (eDate.before(sDate)) {
eDate.add(Calendar.DAY_OF_MONTH, 1);
daysBetween--;
}
return daysBetween;
}
private static Calendar getDatePart(Date date) {
Calendar cal = Calendar.getInstance(); // get calendar instance
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0); // set hour to midnight
cal.set(Calendar.MINUTE, 0); // set minute in hour
cal.set(Calendar.SECOND, 0); // set second in minute
cal.set(Calendar.MILLISECOND, 0); // set millisecond in second
return cal; // return the date part
}
}
Here's a Screenshot on how the app looks like:
DailyTaskRepeater
That's it! It would mean the world to me if someone could help me with all that (especially Problem 1.1!).
Thank you!
The current practice Realm supports is to add an index (e.g. timestamp) and to reverse sort your list for having the latest item at the top and achiving the rearranging effect you are seeking for.
Please consider taking a reference from an adapter example provided in the official repository.
I try to use AsyncTaskLoader in Fragment that is a child of ViewPager. Below a code of my DayFragment:
public class DayFragment extends Fragment
implements LoaderManager.LoaderCallbacks<DayAdapter.DayItem[]> {
private static final int CONTENT_LOADER = 0;
private DayAdapter mAdapter = null;
private int mWeekNumber = 1;
private int mDayCode = 1;
private Table.Timetable mTimetable;
private RecyclerView mRVContent;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final MainActivity mainActivity = (MainActivity) getActivity();
View v = inflater.inflate(R.layout.content, container, false);
mRVContent = (RecyclerView) v.findViewById(R.id.rvContent);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(mainActivity);
mRVContent.setLayoutManager(layoutManager);
mAdapter = new DayAdapter(getActivity());
mRVContent.setAdapter(mAdapter);
//Initialize the cursor loader
getLoaderManager().initLoader(CONTENT_LOADER, null, this).forceLoad();
return v;
}
#Override
public Loader<DayAdapter.DayItem[]> onCreateLoader(final int id, Bundle args) {
if(CONTENT_LOADER == id) {
return new ContentLoader(getContext(), mWeekNumber, mDayCode, mTimetable);
}
return null;
}
#Override
public void onLoadFinished(Loader loader, DayAdapter.DayItem[] items) {
if(CONTENT_LOADER == loader.getId()) {
mAdapter.setIs24HourFormat(SettingsManager.is24HourFormat(getContext()));
mAdapter.clear();
for (DayAdapter.DayItem item : items) {
mAdapter.add(item);
}
mAdapter.notifyDataSetChanged();
if (items.length == 0) {
mRVContent.setBackgroundResource(R.drawable.bg_lesson_empty);
} else {
mRVContent.setBackgroundColor(0xFFFFFFFF);
}
}
}
#Override
public void onLoaderReset(Loader loader) {
}
private static final class ContentLoader extends AsyncTaskLoader<DayAdapter.DayItem[]> {
private final int mWeekNumber;
private final int mDayCode;
private final Table.Timetable mTimetable;
public ContentLoader(Context context, final int weekNumber, final int dayCode,
Table.Timetable timetable) {
super(context);
mWeekNumber = weekNumber;
mDayCode = dayCode;
mTimetable = timetable;
}
#Override
public DayAdapter.DayItem[] loadInBackground() {
DatabaseHandler db = new DatabaseHandler(getContext());
db.openReadable();
List<Table.Lesson> lessons = db.findLessons(mDayCode, mWeekNumber, mTimetable.getId());
DayAdapter.DayItem[] items = new DayAdapter.DayItem[lessons.size()];
for (int i = 0; i < items.length; ++i) {
Table.Lesson lesson = lessons.get(i);
Table.Subject subject = db.getSubject(lesson.getSubjectId());
Table.Teacher teacher = db.getTeacher(lesson.getTeacherId());
if (teacher == null) {
teacher = new Table.Teacher(""); //Empty name
}
items[i] = new DayAdapter.DayItem()
.setId(lesson.getId())
.setTitle(subject.getTitle())
.setSubtitle(teacher.getName()));
}
db.close();
return items;
}
}
}
PageAdapater
public class PageAdapter extends FragmentStatePagerAdapter {
public static final int PAGE_COUNT = 7;
private int mWeekNumber;
private final int[] mDayCodes;
private final String[] mDays;
private final DayFragment[] mFragments = new DayFragment[7];
private Table.Timetable mTimetable;
private boolean mIsRebuildMode = false;
public PageAdapter(Context context, FragmentManager fm,
Table.Timetable timetable, final int weekNumber) {
super(fm);
//Initialize class members
}
#Override
public Fragment getItem(int position) {
DayFragment dayFragment;
if (mFragments[position] == null) {
dayFragment = new DayFragment();
Bundle args = new Bundle();
args.putSerializable(Keys.TIMETABLE, mTimetable);
dayFragment.setArguments(args);
dayFragment.setWeekNumber(mWeekNumber);
dayFragment.setDayCode(mDayCodes[position]);
mFragments[position]= dayFragment;
} else {
dayFragment = mFragments[position];
}
return dayFragment;
}
#Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
//do nothing here! no call to super.restoreState(arg0, arg1);
}
public void setWeekNumber(final int weekNumber) {
mWeekNumber = weekNumber;
Arrays.fill(mFragments, null);
}
public void setIsRebuildMode(final boolean isRebuildMode) {
mIsRebuildMode = isRebuildMode;
}
#Override
public CharSequence getPageTitle(final int position) {
return mDays[position];
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public int getItemPosition(Object object) {
if(mIsRebuildMode) {
return POSITION_NONE;
}
return super.getItemPosition(object);
}
}
But it doesn't call onLoadFinished method. I checked Log output... LoaderManager calls onCreateLoader but it never calls onLoadFinished except first run (when app started and ViewPager shows a first page (Fragment)). It's all! After if I switch a page to a next and a next and return to a first page LoaderManager doesn't call onLoadFinished for the first page too. But it creates loader calling onCreateLoader and resets loader calling onLoaderReset. Is it a joke from Google?
See fragment lifecycle. ViewPager don't re create fragment, just swipe them
-- Start ViewPager
: create
: createView
: start
: resume
not changed if you swipe fragments
--- sleep smartphone
: pause
: stop
--- wakeup smartphone
: start
: resume
: pause
--- Close ViewPager
: stop
: detach
I am working on an application that I expect to migrate to multiple platforms. To facilitate the porting, I'm using Fragments to streamline the process. So what I have right now is a single Activity and several fragments. In one case I have several instances of the same fragment, with different data. Obviously each of these instances of the same fragment will use the same interface functions.
I've added the parameter "tag" to the interface function to identify each instance of fragment (using the tag used in creating the fragment). I guess one could also use the resource ID.
My question: Is this a good way to do this? Is there a better or more accepted method?
Activity code snippet:
public class SailboatRaceActivity extends FragmentActivity
implements SR_CommandsFragment.OnSR_CommandsSelectedListener,
SR_SubCommands_Fragment.OnSR_SubCommandsSelectedListener,
FleetListFragment.FleetList_WidthChange,
FleetListFragment.FleetListChange,
FleetScoringFragment.OnFS_SelectedListener,
FleetSpinnerFragment.FleetSpinner_Selection {
// State Variables
private int mSubCommandPositon;
private int mCommandPosition;
private int mFleetSelection;
private int mFleetListColumnWidth = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mSubCommandPositon = savedInstanceState.getInt("mSubCommandPositon");
mCommandPosition = savedInstanceState.getInt("mCommandPosition");
mFleetSelection = savedInstanceState.getInt("mFleetSelection");
mFleetListColumnWidth = savedInstanceState.getInt("mFleetListColumnWidth");
}
setContentView(R.layout.sailboat_race_layout);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
}
public void onSR_CommandSelected(int position) {
SR_SubCommands_Fragment SR_SubCFrag = (SR_SubCommands_Fragment) getFragmentManager().findFragmentById(R.id.subcommands);
mCommandPosition = position;
if (SR_SubCFrag != null) {
SR_SubCFrag.update_SR_subcommands(position);
SR_SubCFrag.ChangeListViewWidth(mFleetListColumnWidth);
}
}
public void onSR_SubCommandSelected(int position) {
mSubCommandPositon = position;
FleetScoringFragment FSFrag = (FleetScoringFragment) getFragmentManager().findFragmentById(R.id.fleet_scoring);
if (FSFrag != null) {
FSFrag.update_commands(mCommandPosition, mSubCommandPositon);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt("mSubCommandPositon", mSubCommandPositon);
outState.putInt("mCommandPosition", mCommandPosition);
outState.putInt("mFleetSelection", mFleetSelection);
outState.putInt("mFleetListColumnWidth", mFleetListColumnWidth);
super.onSaveInstanceState(outState);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void FleetList_WidthChange(int width) {
mFleetListColumnWidth = width;
// can we set width of other two lists
SR_SubCommands_Fragment SR_SubCFrag = (SR_SubCommands_Fragment) getFragmentManager().findFragmentById(R.id.subcommands);
if (SR_SubCFrag != null) {
SR_SubCFrag.ChangeListViewWidth(width);
}
SR_CommandsFragment SR_CFrag = (SR_CommandsFragment) getFragmentManager().findFragmentById(R.id.commands);
if (SR_CFrag != null) {
SR_CFrag.ChangeListViewWidth(width);
}
}
#Override
public void FleetListChange(int position) {
FleetScoringFragment FSFrag = (FleetScoringFragment) getFragmentManager().findFragmentById(R.id.fleet_scoring);
if (FSFrag != null) {
mFleetSelection = position;
FSFrag.update_FleetScoringList(position);
}
FleetScoringHeaderFragment FSHFrag = (FleetScoringHeaderFragment) getFragmentManager().findFragmentById(R.id.scoring_header);
if (FSHFrag != null) {
mFleetSelection = position;
FSHFrag.update_FleetScoringHeader(position);
}
}
#Override
public void onFS_Selected(int position) {
}
#Override
public void FSpinner_selection(int selection, String tag) {
int fs = selection;
String thistag =tag;
// do something
}
The FSpinner_selection interface is the one I'm working on. I set this implementation just to check if the data was valid.
FleetSpinner snippet:
public class FleetSpinnerFragment extends Fragment {
private int nPresentFleetPosition;
public ArrayList<FleetItem> mListArray = new ArrayList<FleetItem>();
FleetSpinnerAdapter adapter;
FleetSpinner_Selection mCallback;
Spinner spinner;
String tag;
public interface FleetSpinner_Selection {
public void FSpinner_selection(int selection, String tag);
}
public FleetSpinnerFragment() {
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallback = (FleetSpinner_Selection) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement FSpinner_Selection");
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
nPresentFleetPosition = 0;
for (int i = 0; i<Fleet.fleetbit.length;i++) {
mListArray.add(Fleet.fleetbit[i]);
}
} else {
nPresentFleetPosition = savedInstanceState.getInt("nPresentFleetPosition");
ArrayList temp = savedInstanceState.getParcelableArrayList("thisFleetList");
mListArray.addAll(temp);
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("nPresentFleetPosition", nPresentFleetPosition);
ArrayList<FleetItem> temp = new ArrayList<FleetItem>();
for (int i =0 ; i< adapter.getCount();i++){
temp.add(adapter.getItem(i));
}
savedInstanceState.putParcelableArrayList("thisFleetList", temp);
super.onSaveInstanceState(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = new View(getActivity());
adapter = new FleetSpinnerAdapter(getActivity(), R.layout.fleet_list_item);
spinner = new Spinner(getActivity());
spinner.setAdapter(adapter);
adapter.addAll(Fleet.fleetbit);
spinner.setOnItemSelectedListener(new CustomOnItemSelectedListener());
tag = getTag();
spinner.setSelection(nPresentFleetPosition);
return spinner;
}
public void update_FleetScoringHeader(int position) {
nPresentFleetPosition = position;
FrameLayout FLScoringHeader = (FrameLayout) getActivity().findViewById(R.id.scoring_header);
TextView v1 = (TextView) FLScoringHeader.findViewById(R.id.fleet_list_item_textview);
v1.setText(Fleet.fleetbit[nPresentFleetPosition].name);
ImageView v0 = (ImageView) FLScoringHeader.findViewById(R.id.fleet_list_item_icon);
v0.setImageResource(Fleet.fleetbit[nPresentFleetPosition].id);
}
static class FleetHolder {
ImageView imgIcon;
TextView txtTitle;
}
public class CustomOnItemSelectedListener extends Activity implements AdapterView.OnItemSelectedListener {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
FleetItem temp = (FleetItem) parent.getItemAtPosition(position);
nPresentFleetPosition = position;
mCallback.FSpinner_selection(position, tag);
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
}
I'm just starting to learn android programming, so this may be a really noob question.
How can I change what the ActionBar menu contains depending on how many items in a RecyclerView are selected? For instance, when one card is selected in 'selection mode,' I want an Edit icon (pencil) visible. As soon as more than one item is selected, I want the Edit icon to disappear from the options.
The code below: I tried to create a condition in which mDeletionMode creates a menu with an Edit icon present if the number of selected items was <= 1 , and create a menu without the pencil if the number was more than one. My approach was foolish, as I realize that the menu is only created after an item experiences a longClick, and of course only one item would have been selected at that point. I left the dumb-dumb code I used to try this just to show my approach, while what I am actually looking for is the following:
public class SubjectManagerFragment extends BaseFragment implements ActionMode.Callback {
public static ArrayList<SubjectInfo> subjectList = new ArrayList<SubjectInfo>();
public static FloatingActionButton fabCreateSubject;
private AlertDialog.Builder build;
private MultiSelector mMultiSelector = new MultiSelector();
public ActionMode actionMode;
public RecyclerView recList;
public CardView cardView;
public ItemClickSupport itemClick;
// currently an adaptation from:
// https://github.com/bignerdranch/recyclerview-multiselect#modal-multiselection-with-long-click
public ActionMode.Callback mDeleteMode = new ModalMultiSelectorCallback(mMultiSelector) {
#Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
super.onCreateActionMode(actionMode, menu);
if (mMultiSelector.getSelectedPositions().size() <= 1) {
getActivity().getMenuInflater().inflate(R.menu.menu_subject_manager, menu);
} else {
getActivity().getMenuInflater().inflate(R.menu.menu_subject_manager_multiple, menu);
}
return true;
}
#Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.action_select_all:
// Delete crimes from model
//actually do nothing.
mMultiSelector.clearSelections();
return true;
default:
break;
}
return false;
}
};
public static final String ARG_PARAM1 = "param1";
public static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
public static SubjectManagerFragment newInstance(String param1, String param2) {
SubjectManagerFragment fragment = new SubjectManagerFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
public SubjectManagerFragment() {
// Required empty public constructor
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
//non graphical initialization
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(false);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View smFragmentView = inflater.inflate(R.layout.fragment_subject_manager, container, false);
recList = (RecyclerView) smFragmentView.findViewById(R.id.subject_card_list);
cardView = (CardView) smFragmentView.findViewById(R.id.subject_card);
recList.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
llm.setOrientation(LinearLayoutManager.VERTICAL);
subjectList = getSubjectInfoArrayList();
recList.setLayoutManager(llm);
recList.setAdapter(new CrimeAdapter());
fabCreateSubject = (FloatingActionButton) smFragmentView.findViewById(R.id.fab_create_subject);
fabCreateSubject.setOnClickListener (new View.OnClickListener() {
#Override
public void onClick(View v) {
build = new AlertDialog.Builder(getActivity());
LayoutInflater inflater1 = getActivity().getLayoutInflater();
View alertview = inflater1.inflate(R.layout.create_subject_dialog, null);
// Pass null as the parent view because its going in the dialog layout
build.setView(alertview);
final EditText inputSubjectName = (EditText) alertview.findViewById(R.id.dialog_edit_subject_card_name);
final EditText inputSubjectGrade = (EditText) alertview.findViewById(R.id.dialog_edit_subject_card_grade);
build.setTitle("Add Subject");
build.setPositiveButton("Save", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
String enteredSubjectName = inputSubjectName.getText().toString();
int enteredSubjectGrade = Integer.parseInt("0" + inputSubjectGrade.getText().toString()); //was getting stupid error from null value going to int?
boolean enteredSubjectIsArchived = false;
if (subjectCanBeEntered(inputSubjectName, inputSubjectGrade, subjectList)) {
SubjectInfo si = new SubjectInfo(enteredSubjectName, "Assignments", enteredSubjectGrade, enteredSubjectIsArchived, true);
si.save();
subjectList.add(si);
getActivity().recreate();
sa.notifyDataSetChanged();
recList.smoothScrollToPosition(subjectList.size()-1);
} //will need to check if subject already exists, but YOLO for now.
dialog.cancel();
}
});
build.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = build.create();
alert.show();
}
});
// Inflate the layout for this fragment
return smFragmentView;
}
public ArrayList<SubjectInfo> getSubjectInfoArrayList(){
ArrayList<SubjectInfo> sial= new ArrayList<SubjectInfo>();
List<SubjectInfo> sil = SubjectInfo.listAll(SubjectInfo.class);
sial.addAll(sil);
for (int go = 0; go <sial.size(); go++) {
if (sial.get(go).itemHeaderTitle.equals("Archived")) {
sial.remove(go);
}
}
return sial;
}
public boolean subjectCanBeEntered (EditText inputName, EditText inputGrade, ArrayList<SubjectInfo> aList) {
boolean enterable = true;
if ((inputName.getText().toString().equals(""))) {
enterable = false;
Toast.makeText(
getActivity().getApplicationContext(), "Enter a class name.", Toast.LENGTH_SHORT).show();
}
if ((inputGrade.getText().toString().equals(""))) { // I don't think hint is picked up
enterable = false;
Toast.makeText(
getActivity().getApplicationContext(), "Enter a grade.", Toast.LENGTH_SHORT).show();
}
for (int go = 0; go < aList.size(); go++) {
if (inputName.getText().toString().equals(aList.get(go).subjectName)) {
enterable = false;
Toast.makeText(
getActivity().getApplicationContext(), "You've already saved a class with that name.", Toast.LENGTH_LONG).show();
}
}
return enterable;
}
private class CrimeHolder extends SwappingHolder
implements View.OnClickListener, View.OnLongClickListener {
protected TextView vSubjectName;
protected EditText vSubjectGrade;
protected RelativeLayout vSubjectLayout;
private SubjectInfo sInfo;
public CrimeHolder(View itemView) {
super(itemView, mMultiSelector);
vSubjectName = (TextView) itemView.findViewById(R.id.subject_card_name_textView);
vSubjectGrade = (EditText) itemView.findViewById(R.id.subject_card_grade_editText);
vSubjectLayout = (RelativeLayout) itemView.findViewById(R.id.subject_card_relative_layout);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
itemView.setLongClickable(true);
}
public void bindCrime(SubjectInfo si) {
sInfo = si;
vSubjectName.setText(sInfo.subjectName);
vSubjectGrade.setText(Integer.toString(si.subjectGrade));
vSubjectLayout.setBackgroundColor(Color.parseColor(SubjectAdapter.giveSubjectHexValue((double) si.subjectGrade)));
}
#Override
public void onClick(View v) {
if (sInfo == null) {
return;
}
if (!mMultiSelector.tapSelection(this)) {
// This condition is the same as, if not in ActionMode, handle the click normally:
//getActionBar().startActionMode(mDeleteMode);
}
}
#Override
public boolean onLongClick(View v) {
//ActionBarActivity activity = (ActionBarActivity)getActivity();
getActionBar().startActionMode(mDeleteMode);
mMultiSelector.setSelected(this, true);
return true;
}
}
private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> {
#Override
public CrimeHolder onCreateViewHolder(ViewGroup parent, int pos) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.subject_card_layout, parent, false);
return new CrimeHolder(view);
}
#Override
public void onBindViewHolder(CrimeHolder holder, int pos) {
SubjectInfo sInfo = subjectList.get(pos);
holder.bindCrime(sInfo);
}
#Override
public int getItemCount() {
return subjectList.size();
}
}
}
SO my question is: how to programmatically either remove/add the Edit icon OR switch to the other menu (with one fewer option) depending on how may items are selected.
Use an ActionMode instance to manipulate the ActionBar menu.
#Override
public boolean onLongClick(View v) {
this.actionMode = getActionBar().startActionMode(mDeleteMode);
mMultiSelector.setSelected(this, true);
return true;
}
Below is where your menuItem gets initialized. Keep in mind that onCreateActionMode resides in the creation of the new ModalMultiSelectorCallback.
#Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
super.onCreateActionMode(actionMode, menu);getActivity().getMenuInflater().inflate(R.menu.menu_subject_manager, menu);
mEditItem = actionMode.getMenu().findItem(R.id.action_edit);
mActionMenu = actionMode.getMenu();
return true;
}
Now, mEditItem can be removed or replaced with ease, as mEditItem is an instance variable. I'll provide my implementation in my onClick()
#Override
public void onClick(View v) {
if (sInfo == null) {
return;
}
if (!mMultiSelector.tapSelection(this)) {
// if not in selection mode, this is handled
}
if (mMultiSelector.tapSelection(this)) {
mMultiSelector.tapSelection(this);
switch (mMultiSelector.getSelectedPositions().size()) {
case 0:
actionMode.finish();
break;
case 1:
mEditItem.setVisible(true);
break;
case 2:
mEditItem.setVisible(false);
break;
default:
break;
}
}
}
And there I have it. For me, I just needed an item to disappear/reappear according to how many items were selected at any given moment. The .setVisible() method took care of this for me. I hope this helps someone with a similar issue!