I implemented a navigation drawer and had used fragments. Now i can switch to fragments from the drawer menu. But when i press back, my app closes from that fragment and don't know how to go back to the main activity.
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
displaySelectedScreen(id);
return true;
}
private void displaySelectedScreen(int id) {
Fragment frag = null;
switch (id) {
case R.id.trigger:
break;
case R.id.setup:
if (android.os.Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(Color.parseColor("#7B1FA2"));
}
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#8E24AA")));
frag = new SettingsF();
break;
case R.id.recordings:
if (android.os.Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(Color.parseColor("#4E342E"));
}
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#8D6E63")));
break;
case R.id.howto:
if (android.os.Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(Color.parseColor("#7B1FA2"));
}
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#8E24AA")));
break;
case R.id.about:
if (android.os.Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(Color.parseColor("#7B1FA2"));
}
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#8E24AA")));
break;
case R.id.email_us:
if (android.os.Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(Color.parseColor("#7B1FA2"));
}
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#8E24AA")));
break;
}
if (frag != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_main, frag);
ft.addToBackStack(ft.getClass().getSimpleName()).commit();
}
DrawerLayout drawer = findViewById(R.id.drawer);
drawer.closeDrawer(GravityCompat.START);
}
And here is the setting fragment only:
public class SettingsF extends ListFragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_settings, container, false);
String[] datasource = {"Trusted Contacts", "Custom Text Set-Up"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), R.layout.rowlayout, R.id.txtitems, datasource);
setListAdapter(adapter);
setRetainInstance(true);
return rootView;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getActivity().setTitle("Context Set-Up");
}
public void onListItemClick(ListView l, View view, int position, long id) {
ViewGroup viewGroup = (ViewGroup) view;
TextView tv = (TextView) viewGroup.findViewById(R.id.txtitems);
Toast.makeText(getActivity(), tv.getText().toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onResume() {
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener( new View.OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
//Already Used Intent, doesn't work
return true;
}
return false;
}
} );
super.onResume();
}
}
I updated the question. I already used addToBackStack(), then overrided the method onResume() but still the app terminates when pressing the back button.
I'm really that new to this, so don't misunderstand me what did i implemented throughout the codes.
Thanks In Advance
for Hardware backbutton
add this code in onResume method in fragment
//You need to add the following line
fragment.getView().setFocusableInTouchMode(true);
fragment.getView().requestFocus();
fragment.getView().setOnKeyListener( new OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
//here use intent to call mainActivity
return true;
}
return false;
}
} );
hope this will help you
Add this line after ft.replace():
ft.addToBackStack(null);
This line will make it so that each time you select an item from the navigation drawer, a history of fragments is built up. Then when you press the back button, you'll go back through the history. If you have no history items, the app will close.
A more sophisticated implementation might use a name argument (like "FragA" or "FragB") instead of null as the argument for addToBackStack(), and then check to see if your fragment already exists in the back stack instead of always opening a new one. That way if you went A->B->A->B->A->B you wouldn't have three copies of A and two copies of B in the history.
Related
My recyclerview's adapter continuously calls getItemViewType() and seems to be no sign to stop.
I also print log in onBindViewHolder(), onCreateViewHolder(), and viewHolder's bind function, but only getItemViewType get called infinitely.
How can I solve this problem?
My Fragment:
I added log in each function, and didn't see any function be called continuously.
public class Fragment{
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (presenter == null) {
presenter = new Presenter(this);
}
initViewModel();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
setRecyclerView();
//this will modify adapter
loadData();
return rootView;
}
void initViewModel(){
messageViewModel = new ViewModelProvider(this).get(messageViewModel.class);
final Observer<ArrayList<WallPost>> wallPostObserver = wallPostList -> {
//this will modify adapter
updateRecyclerView(wallPostList);
};
messageViewModel.getWallPostsLiveData().observe(this, wallPostObserver);
}
public void setRecyclerView() {
layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager);
adapter = new Adapter(getContext(), new ArrayList<>(), Repository.getInstance().getMemberData().getValue(), getType());
recyclerView.setAdapter(adapter);
recyclerView.addOnScrollListener(scrollListener);
}
}
My Adapter getItemViewType(int position) function:
Adapter getItemCount() be called continuously same as getItemViewType(int position).
#Override
public int getItemViewType(int position) {
if (wallPostList.size() == 0) {
return VIEW1;
}else if(position >= wallPostList.size()){
return VIEW7;
}
WallPost wallPost = wallPostList.get(position);
Log.d("Adapter","position:"+position);
if(wallPost.getStatus().equals(POST_STATUS_REVOKE)){
return VIEW8;
}
switch (wallPost.getType()) {
case azz:
return VIEW5;
case axx:
return VIEW6;
case ayy:
default:
if(wallPost.getMsgItemList().size() > 0){
return VIEW4;
}else if(StringUtils.checkHasLink(wallPost.getContent())){
return VIEW3;
}else {
return VIEW2;
}
}
}
Update: cause problem's code
I found the problem cause this, I solve this problem by add a flag in my customize view to restrict not repeat onMeansure, but I don't know why this will make adapter repeat call getItemViewType(int position) and getItemCount().
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(type.equals(SINGLE)){
info.post(()->{
halfWidth = (info.getMeasuredWidth()- arrowImageView.getMeasuredWidth())/2;
name.setMaxWidth(halfWidth-image.getMeasuredWidth());
tvNoticeTime.setMaxWidth(halfWidth-image.getMeasuredWidth());
});
}
else if(type.equals(MULTI)){
info.post(()->{
name.setMaxWidth(info.getMeasuredWidth()-image.getMeasuredWidth() - (int)PixelUtil.convertDpToPixel(8,context));
});
}
else{
int dis = (int)PixelUtil.convertDpToPixel(8,context);
info.post(()->{
name.setMaxWidth(info.getMeasuredWidth()-image.getMeasuredWidth()-dis-
infoImageView.getMeasuredWidth()-dis);
});
}
}
The problem you are facing is that you update your RecyclerView based on the wallPostObserver that looks for changes in the wallPostList. There could be a problem regaring this because if the List changes there is a chnage in the recycler View and the getItemViewType method is called. If you made it so that the recyclerView is only updated once the user chooes to or in fix intervals you wouldn't have the problem. You could fix that by creating a Method that triggers if the List changes (without using an Observer) there you could define the max update interval and make is so that you dont always throw away your entire list.
I am trying to make a social media app , Im using fragments within BottomNavigation (home - smart room - notifications - profile), And Im using a floating button to switch to "Add post" activity , Now Im having a problem with the back button , i want it to take me to the recent visited fragment but it always take me to the home fragment
here is my BottomNavigation activity code :
public class BottomNavigationActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bottom_navigation);
BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation_layout);
//setEnabled(false) for the 2nd index because we're using a floating button instead
bottomNav.setBackground(null);
bottomNav.getMenu().getItem(2).setEnabled(false);
bottomNav.setOnItemSelectedListener(navListener);
//start AddPostActivity class
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(getApplicationContext(), AddPostActivity.class));
finish();
}
});
}
//Switch between fragments
public BottomNavigationView.OnItemSelectedListener navListener =
new BottomNavigationView.OnItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.nav_home:
selectedFragment = new HomeFragment();
break;
case R.id.nav_sr:
selectedFragment = new SmartRoomFragment();
break;
case R.id.nav_notif:
selectedFragment = new NotificationsFragment();
break;
case R.id.nav_profile:
selectedFragment = new ProfileFragment();
break;
}
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
selectedFragment).commit();
return true;
}
};
}
I want the best solution to handle this problem , thanks in advance!
mainActivity has button. After button is clicked a popup message show having a button and a TextInputLayout.
data is sent to a tabbed acticity having 2 fragments and
the data is shown in textview of first fragment. Error is the data wont show in the textview while running
adapter for 2 fragments--
public class adapter extends FragmentPagerAdapter {
public adapter(#NonNull FragmentManager fm) {
super(fm);
}
#NonNull
#Override
public Fragment getItem(int position) {
Fragment fragment;
if (position == 1) {
fragment = new SecondFragment();
} else {
fragment = new FirstFragment();
}
return fragment;
}
#Override
public int getCount() {
return 2;
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
String title = null;
if (position == 0) {
title = "TextView";
} else if (position == 1) {
title = "Listview";
}
return title;
}
}
mainActivity(having popup window on clicking button)
myDialog = new Dialog(this);
}
//for for joining match
public void ShowPopup(View view) {
myDialog.setContentView(R.layout.popup);
myDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
myDialog.show();
name = myDialog.findViewById(R.id.ig_name);
String igname = Objects.requireNonNull(name.getEditText()).getText().toString();
entry = myDialog.findViewById(R.id.entry);
entry.setOnClickListener(v -> {
Intent intent = new Intent(getApplicationContext(), secondActivity.class);
intent.putExtra("igname", igname);
startActivity(intent);
finish();
});
}
}
my firstFragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_first, container, false);
secondActivity activity = (secondActivity) getActivity();
TextView output = view.findViewById(R.id.textshow);
Bundle results = activity.getMyData();
String value1 = results.getString("value");
output.setText(value1);
return view;
}
secondActivity (intent is transfered from popup window)
public class secondActivity extends AppCompatActivity {
private String name;
TabLayout tabLayout;
ViewPager viewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(new adapter(getSupportFragmentManager()));
tabLayout = findViewById(R.id.tabs_layout);
tabLayout.setupWithViewPager(viewPager);
Intent intent = getIntent();
name = intent.getExtras().getString("igname");
}
public Bundle getMyData() {
Bundle bundle = new Bundle();
bundle.putString("value", name);
return bundle;
}
}
Get the "igname" String value in onClickListner & also dismiss the dialog before finishing the activity.
entry.setOnClickListener(v -> {
String igname = Objects.requireNonNull(name.getEditText()).getText().toString();
Intent intent = new Intent(getApplicationContext(), secondActivity.class);
intent.putExtra("igname", igname);
startActivity(intent);
finish();
});
Here I'm using Custom Adapter by implementing listAdapter to Support buttons in the list items. When i click the Delete button it deletes currect row. But i can't able to refresh this listView.
File Structure is MainActivity -> FragmentClass-> this Custom class for the listAdpater.
public class Category_ListView_Custom_Adapter extends BaseAdapter implements ListAdapter{
private Realm realm;
private ArrayList<String> list = new ArrayList<>();
private Context context;
public Category_ListView_Custom_Adapter(ArrayList<String> list, Context context){
this.list = list;
this.context = context;
}
#Override
public int getCount() {
return list.size();
}
#Override
public Object getItem(int i) {
return list.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(final int i, View view, ViewGroup viewGroup) {
View view1 = view;
realm = Realm.getDefaultInstance();
if(view == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.category_listview_row, null);
}
TextView listItemText = (TextView) view.findViewById(R.id.item);
listItemText.setText(list.get(i));
ImageView edit = (ImageView) view.findViewById(R.id.image);
ImageView delete = (ImageView) view.findViewById(R.id.delete);
edit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// list.remove(i);
/// Toast.makeText(view.getContext(), "hey "+list.get(i).toString(), Toast.LENGTH_SHORT).show();
// notifyDataSetChanged();
Intent intent = new Intent(context, AddCategory.class);
intent.putExtra("category","expense");
intent.putExtra("subcategory", list.get(i));
context.startActivity(intent);
}
});
delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
RealmResults<Category> categorylist = realm.where(Category.class).equalTo("Id",i+1).and().equalTo("Subcategory",list.get(i)).findAll();
for (Category category : categorylist) {
realm.beginTransaction();
categorylist.deleteFirstFromRealm();
realm.commitTransaction();
try {
context.notifyAll();
}catch (Exception e){
Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
}
// Expense.call_resume
}
}
});
return view;
}
}
Please Help me. Thank you.
You are .removing from a wrong view. Try to make root List<> static and do Class.list.get(i).remove or something like that.
Also, try to hide a view inside your get view.
In my android application, I am trying to show contextual action bar (CAB) in listview for item delete. The problem is that whenever an item is pressed for long, the item gets selected, but CAB doesn't appear. I have verified with so many tutorials, can't figure out what am I missing. Any help would be appreciated.
Adapter Class
public class DuasListAdapter extends ArrayAdapter<String>{
// class variables
private final Context context;// to save context
private final List<String> duas;// to save list of stores
LayoutInflater inflater;//
private SparseBooleanArray selectedItemsIds;
public DuasListAdapter(Context ctx,int resourceId, List<String> duasList) {
super(ctx, resourceId, duasList);
selectedItemsIds = new SparseBooleanArray();
context = ctx;// save context
duas = duasList;// save list of stores
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);// save inflater layout
}
*/
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if(convertView==null){
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.adapter_list_duas, null);
convertView.setTag(holder);
holder.duaIndex = position;
holder.background = (LinearLayout) convertView.findViewById(R.id.ll_duas_list);
holder.iv_duatype = (ImageView) convertView.findViewById(R.id.iv_duatype);
holder.tv_dua_arabic = (TextView) convertView.findViewById(R.id.tv_dua_arabic);
holder.tv_dua_no = (TextView) convertView.findViewById(R.id.dua_no);
}
else {
holder = (ViewHolder) convertView.getTag();
}
return convertView;
}
static class ViewHolder {
int duaIndex;
LinearLayout background;// background to display color for read and unread messages
ImageView iv_duatype;
TextView tv_dua_arabic;// title of message
TextView tv_dua_ref;// message by and message created on
TextView tv_dua_no;
/**
* #description constructor to save context and list of stores views
* #param v to refer the view
* #return
*/
}
public void remove(List<String> object){
duas.remove(object);
notifyDataSetChanged();
}
public List<String> getDuas(){
return duas;
}
public void toggleSelection(int position) {
selectView(position, !selectedItemsIds.get(position));
}
public void removeSelection() {
selectedItemsIds = new SparseBooleanArray();
notifyDataSetChanged();
}
public void selectView(int position, boolean value) {
if (value)
selectedItemsIds.put(position, value);
else
selectedItemsIds.delete(position);
notifyDataSetChanged();
}
public int getSelectedCount() {
return selectedItemsIds.size();
}
public SparseBooleanArray getSelectedIds() {
return selectedItemsIds;
}
}
Fragment Class
Context context;
LinearLayout ll_back_duas_list_header;
TextView ll_back_duas_list_header_title;
ListView lvDuaas;
public static DuasListAdapter duasAdapter;
ActionMode mAction;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
context = inflater.getContext();
View view = inflater.inflate(R.layout.fragment_list_duas, container, false);
ll_back_duas_list_header = (LinearLayout) view.findViewById(R.id.ll_back_duas_list_header);
ll_back_duas_list_header_title = (TextView) view.findViewById(R.id.ll_back_duas_list_header_title);
lvDuaas = (ListView) view.findViewById(R.id.lv_duas);
setHasOptionsMenu(true);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
String verses = new SharedPreferencesSupplication().read(SingletonClass.keyListOfVerses, "a1");
String[] versesList = verses.split(",");
final ArrayList<String> duas = new ArrayList<String>();
for (int i = 0; i < versesList.length; i++) {
if (versesList[i].length() > 0)
duas.add(versesList[i]);
}
duasAdapter = new DuasListAdapter(context,R.layout.adapter_list_duas,duas);
lvDuaas.setAdapter(duasAdapter);
lvDuaas.setOnItemLongClickListener(new OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent,
View view, int position, long id) {
// TODO Auto-generated method stub
lvDuaas.setChoiceMode(lvDuaas.CHOICE_MODE_MULTIPLE);
lvDuaas.setItemChecked(position, true);
mAction = getActivity().startActionMode(modeCallBack);
view.setSelected(true);
return true;
}
});
super.onActivityCreated(savedInstanceState);
}
ActionMode.Callback modeCallBack = new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
mode.getMenuInflater().inflate(R.menu.activity_main, menu);
return false;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode,
MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case R.id.delete:
// Calls getSelectedIds method from ListViewAdapter Class
SparseBooleanArray selected = duasAdapter
.getSelectedIds();
// Captures all selected ids with a loop
for (int i = (selected.size() - 1); i >= 0; i--) {
if (selected.valueAt(i)) {
String selecteditem = duasAdapter.getItem(selected.keyAt(i));
// Remove selected items following the ids
duasAdapter.remove(selecteditem);
}
}
// Close CAB
mode.finish();
return true;
default:
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode mode) {
// TODO Auto-generated method stub
duasAdapter.removeSelection();
}
};
}
XML file
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
>
<item
android:id="#+id/delete"
android:title="#string/delete"
/>
</menu>
From looking at your code I can see the onCreateActionMode callback function returns false.
onCreateActionMode should return true if the action mode should be created, false if entering this mode should be aborted.
So when the user long clicks on the ListItem and the event is triggered it reaches onCreateActionMode and returns false. Telling it to abort. Hence no CAB menu appears. Try changing it to true and also adding in some logs to see if it ever makes it into the callback function.
This would be the change below:
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
Log.d("Test","In onCreateActionMode");
mode.getMenuInflater().inflate(R.menu.activity_main, menu);
return true;//code updated here
}
Android Link to reference : http://developer.android.com/reference/android/view/ActionMode.Callback.html#onCreateActionMode(android.view.ActionMode, android.view.Menu)