I got a few fragments and I tried to sort out the transitions between them. I can go from Main -> A -> B -> C. Then once I am done with the stuffs in C, I wanted to go back to B -> A -> Main. This is the desired transition I wanted to achieve.
However, with my current code, there is something weird with the transition. I go from Main -> A -> B -> C, then inside C I am doing some SQL to create data in database, once I am done, I go back to B. However, from there, when I pressed back button, it go back to C -> B -> A -> Main. There is an unnecessary C in the back transition.
Inside my Main, I am calling A like this:
final SettingActivity settingFragment = new SettingActivity();
ft.replace(R.id.frame,settingFragment);
ft.addToBackStack("tag");
ft.commit();
Inside my A, I am calling B like this:
FragmentTransaction it = getFragmentManager().beginTransaction();
it.replace(R.id.frame,categoryFragment);
it.addToBackStack("tag");
it.commit();
Inside my B, I am calling C like this:
FragmentTransaction et = getFragmentManager().beginTransaction();
et.replace(R.id.frame,editFragment);
et.addToBackStack("tag");
et.commit();
Then inside my C, when I am successfully inserted a record in database, I am calling B like this:
// button on click listener
new GetAllCategoriesAsyncTask(
new GetAllCategoriesAsyncTask.OnRoutineFinished() {
public void onFinish() {
Bundle bundle = new Bundle();
FragmentTransaction it = getFragmentManager().beginTransaction();
bundle.putSerializable("categorylist", GetAllCategoriesAsyncTask.categoryList);
categoryFragment.setArguments(bundle);
it.replace(R.id.frame,categoryFragment);
it.addToBackStack("tag");
it.commit();
}
}).execute();
Then inside my B, I am getting the data like this in onCreate():
categoryList = (ArrayList<Category>) getArguments().getSerializable("categorylist");
On button click pop the current fragment from stack which is C , instead of adding B again in the stack. So, replace the button onClick code with below line of code:
getFragmentManager().popBackStack();
Bundle is used when you transferring data to next screen. For transferring data to previous screen you need to use callbacks.
For reference please find below attached link :Communicating with Other Fragments
Related
I have an application that should work on a tablet, and there's a page where a recyclerview is present and in its row item there is 2 edit texts (with numberDecimal as input), the default keyboard shouldn't appear because it's covering a considerable large portion of screen.
I created buttons in the activity to act like the keyboard buttons however the problem is how to make the button from activity to communicate with the edit texts in the adapter.
how can i know that if I press (Button "1") for example that it should display 1 in the focused edittext in the adapter, and how if I pressed "<" or ">" button it should know the previous and next edit texts
please help
I'll provide example in Kotlin, I assume that is what you are using.
So you can simply override the key handling in the parent class like:
Make a method that you call one time from onCreate like:
fun keydown_onClick(){
try{
myActivity.setOnKeyListener { _, keyCode, event ->
if (keyCode == android.view.KeyEvent.KEYS_YOU_CARE_ABOUT) {
//handle it
if(activeViewHolder != null){
var displayText = activeViewHolder.yourEditText.text
keyPressedChar = (char)event.getUnicodeChar()+""
//if it's a special key you care about, then handle it in a when statement or if and pass to your adapter if needed to go to next row for example.
displayText += keyPressedChar
activeViewHolder.yourEditText.text = displayText
}
return#setOnKeyListener true//we've processed it
} else
return#setOnKeyListener false// pass on to be processed as normal
}
}catch (ex: Exception){
A35Log.e(mClassTag, "Error handling onkeydown listener: ${ex.message}")
}
}
Next up is how do you handle it. well you should keep track of the active row. You can do this by creating a callback in your activity that gets notified when a different row is selected or gains focus.
interface IFocusChangeListener {
fun onNewItemFocused(holder: MyApdaterViewHolder, item: DataItem, index: Int)
}
This will be passed into your adapter and used to fire back to your activity class.
//in activity
var activeViewHolder: MyAdapterViewHolder? = null
var activeIndex: Int? = null
var activeItem: DataItem? = null
fun onNewItemFocused(holder: MyAdapterViewHolder, item: DataItem, index: Int){
activeViewHolder = holder
activeIndex = index
activeItem = item
}
//now in your key down event you simply pass through the value to the editText in the activeViewHolder,
So last piece is in the adapter.
//on bind View
create View holder, the usual bloat.
Then when you have your editText you simply add.
var item = currentDataItem
var index = currentDataItemIndex
var viewHolder = currentViewHolder //from onBind
viewHolder.setOnFocusChangeListener(object: View.OnFocusChangeListener{
override fun onFocusChange(v: View?, hasFocus: Boolean) {
if(hasFocus){
mFocusListener?.onNewItemFocused(viewHolder, item, index)
}
}
})
//in adapter you may also need
fun onNextEditTextClicked(item: DataItem, index: Int){
//get next data Item by index + 1 if exist and get it's viewholder.editText and set focus to it, this will automatically trigger the focus event back to the activity
}
fun onPreviousEditTextClicked(item: DataItem, index: Int){
//get next data Item by index - 1 if exist and get it's viewholder.editText and set focus to it, this will automatically trigger the focus event back to the activity
}
Now you have the focused viewholder in your calling activity, you can catch the keys you care to catch, probably all of them. You can pass them in, you should be good to go.
NOTE*
For the record, if you are using modern practices, aka Data Binding, then you should be able to just update a bindable string in your model and it will show up on the screen without having to pass it around. You could also bind the focus to being selected and just update the selected boolean of the models. There are cleaner ways to do this if you use binding. But for now, just helping you without complicating it.
This solution may need tweaked, I just typed it here so could be off a bit, but should mostly get you there.
Happy Coding.
Intiliased the Adapter before clicking the buttons. because, required the instance of your Adapter to perform.
create a method add() in your Adapter
ArrayList<String> dataList = new ArrayList<>();
public void add(String element) {
dataList.add(element);
notifyItemInserted(dataList.size() - 1);//this will update the recyclerview with data inserted
}
from your button click call this method with your Adapter instance
yourAdapter.add(btn1.getText().toString());
I have an application that has this transition:
A -> B -> C -> D-> C
Upon entering C , i have to check a flag. Then I have to pass it as intent (let us say intentX = false) to D. After doing something in D , it will then go back to C after pressing a button.
What i did was just pass again the intentX with value true, then startActivity C again.
So what happen is that it created another Activity C.
What i want to happen is that i will not have to start a new Activity C, but use the previous C by just calling super.onBackPressed(). But I cannot pass the new value of the intentX. Is there other way, to achieve what i want. I might have missed some.
What you want is startActivityForResult(). When you go from C to D, instead of using startActivity() use instead startActivityForResult(). Then when you want to return from D to C you use setResult() which can include an Intent object with extras to pass back to C.
I don't recommend doing this in onBackPressed() if you don't have to because this will not be what the user expects. Instead, you should return with this data with an event such as a Button click.
So, in C you will do something like
Intent i = new Intent(new Intent(C.this, D.class);
startActivityForResult(i, 0);
then in D when you are ready to return
Intent i = new Intent();
i.putExtra(); // insert your extras here
setResult(0, i);
then when you return to C you will enter this method (taken from the Docs)
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == RESULT_OK) {
// A contact was picked. Here we will just display it
// to the user.
startActivity(new Intent(Intent.ACTION_VIEW, data));
/*
can also get the extra sent back through data
using data.getStringExtra("someKey");
assuming the extra was a String
*/
}
There are some cases where startActivityForResult is not really needed or it is not practical to change all startActivity calls for startActivityForResult.
If the simple case of just starting a previous activity 'again' is needed, my recommendation is: Use the FLAG_ACTIVITY_CLEAR_TOP flag.
Quoting a brief description:
If set, and the activity being launched is already running in the
current task, then instead of launching a new instance of that
activity, all of the other activities on top of it will be closed and
this Intent will be delivered to the (now on top) old activity as a
new Intent.
For example, consider a task consisting of the activities: A, B, C, D.
If D calls startActivity() with an Intent that resolves to the
component of activity B, then C and D will be finished and B receive
the given Intent, resulting in the stack now being: A, B.
So this example
// From ActivityD
Intent intent = new Intent(getApplicationContext(), ActivityB.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // The flag we wanted
intent.putExtra(ActivityB.SOME_EXTRA_THAT_I_NEED_CHANGED, SomeValue); // Example of changing the intent to get something new..
startActivity(intent);
Where you will get that new intent is defined by which launch mode and which flags where used to start it (in this case our ActivityB).
The currently running instance of activity B in the above example will
either receive the new intent you are starting here in its
onNewIntent() method, or be itself finished and restarted with the new
intent. If it has declared its launch mode to be "multiple" (the
default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same
intent, then it will be finished and re-created; for all other launch
modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be
delivered to the current instance's onNewIntent().
In the onCreate() method of my class I make a grid of buttons, and give them tags to identify them, for example:
button.setTag("one four");
This is working fine. Now I want to make a new temporary button within a method, and I'm using this code:
String s = "one four";
Object o = s;
View view = new View(this);
Button button = (Button)view.findViewWithTag(o);
But button always comes out as "null". And I don't know why.
You must call view.addChild(button); before view.findViewWithTag(o);
And you dont need to do this Object o = s;, view.findViewWithTag(s); will do the same.
View view = new View(this); - you create a new instance of View.
Object view does not have any children. You must call findViewWithTag(s) method from layout which contains your Button object.
Try not assigning the string to the object variable and set the tag directly to be your string.
Mavix, findViewWithTag traverse all child views and works fine in ViewGroup. Try this:
// after button.setTag("one four");
ViewGroup v = (ViewGroup) findViewById(R.id.myFirstLayoutIdInXmlLayoutFile);
Button b = (Button) v.findViewWithTag("one four");
I had the same doubt. In my situation, I have a Main Layout and a Secondary Layout (inside the Main) - the two were RelativeLayout's - and I want to get the components I had added on the screen.
But I had to use dynamic keys (which could be repeated)and were the unique parameter I could use to Identify the components.
Like Natali, in her response, I use "TAG" in the components and worked for me. See below (using Button as example):
Step 1: Declare a button-type variable. Button btn = new Button(this); // this is Context of my Activity
Step 2: Set any key. String any_key = "keyToGetButton";
Step 3: Set tag (key setted in step 2) to your button. btn.setTag(any_key);
Step 4: Get your button by tag (in other method, for example). Button button = (Button) your_layout_where_is_button.findViewWithTag(any_key);
I have application where Activity A allows to set some options and starts (after click on button) Activity B. User can do something in B and when he finishes, he has choice (RadioButtons):
repeat - it means the B Activity will run again with the same options (taken from A),
new - it means application finishes B Activity and goes back to A, where user can set options again and start B again,
end - it goes out from application (I suppose it should finish B and then A Activity).
First I have done this way:
Intent intent = getIntent();
finish();
startActivity(intent);
Another way I could use is to clean all parameters in this Activity, but above was more quickly.
Second is just a finish().
Third is the biggest problem and i don't know how to to this. I tried with startActivityForResult(), onActivityResult() and setResult() but i saw it's impossible to set different results depending on selected RadioButton.
Other method I found is
public static void closeAllBelowActivities(Activity current) {
boolean flag = true;
Activity below = current.getParent();
if (below == null)
return;
System.out.println("Below Parent: " + below.getClass());
while (flag) {
Activity temp = below;
try {
below = temp.getParent();
temp.finish();
} catch (Exception e) {
flag = false;
}
}
}
source
But don't know how to put in current Activity.
Could you help me with this?
"i saw it's impossible to set different results depending on selected RadioButton" isn't really clear to me. Anyway, I suggest you what I would do: Activity A starts Activity B with startActivityForResult() and A wait for a bundle coming from B. When you build the exit function from B to A, create a bundle with your result. In A, you will analyze the bundle coming from B and decide what to do with. If you would exit, call finish().
Edit: Ok this could be your case:
Activity A:
public class XY extends Activity {
// you need that as a flag to go back and forth:
protected static final int SUB_ACTIVITY_REQUEST_CODE_04 = 5337; // choose the number you want...
int result_from_B; // I usually use an int, choose as you want....
static final String KEY_FROM_B = "mKey";
#Override
protected void onActivityResult(int result,int resultCode,Intent data) {
super.onActivityResult(result, resultCode, data);
if (result == SUB_ACTIVITY_REQUEST_CODE_04) {
if (data!=null){
Bundle extras = data.getExtras();
result_from_B = extras.getInt(XY.KEY_FROM_B);
if (result_from_B==1) {
// do what you want
} else {
// do somthing else...
}
}
To call the Activity B, use this:
Intent i = new Intent(this,
ActB.class);
this.startActivityForResult(i,SUB_ACTIVITY_REQUEST_CODE_04);
In the Activity B:
public class ActB extends Activity{
protected final int SUCCESS_RETURN_CODE = 1; // you need it to come back
int result; // this is the result to give to activity A
}
// in the function to build the output:
// I suppose you have already put something in result:
Bundle bundle = new Bundle();
bundle.putInt(XY.KEY_FORM_B, result);
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
ActB.this.setResult(SUCCESS_RETURN_CODE,mIntent);
finish();
This is basic. You can manage also booleans back and forth, arrays, all you want (admitted by a bundle). Just put it in a bundle and go :)
Don't forget to declare your classes (activities) in the Manifest otherwise it will throw an exception runtime.
First of all, you need to keep track of the Intent used to call B. Try the putExtra() method of the intent to B. You can also package everything into a Bundle and restore it with getExtras() (if I recall correctly, that's the method name). When on B, read the Intent used to call it and save the parameters. You also need to startActivityForResult(B) for the following to work.
repeat - it means the B Activity will run again with the same options
(taken from A),
You probably want to call, from B, Activity B again using the FLAG_ACTIVITY_SINGLE_TOP flag in this case. I assume you don't want to have two B instances. Otherwise, just don't use the flag.
Put the Bundle you received from the Intent (from A) again, and catch it (if using single top) in the onNewIntent() method (or just normally, onCreate, if not single top). It goes like this: B -> B onPause() -> B onNewIntent() -> B onResume().
new - it means application finishes B Activity and goes back to A,
where user can set options again and start B again,
Depending on the exact behavior you want, you could call A with FLAG_ACTIVITY_REORDER_TO_FRONT. In this case, you end up with A in the foreground and B in the background (pressing back will go back to B).
Or you could call finish() if you just don't want B anymore, and want to go back to A.
end - it goes out from application (I suppose it should finish B and
then A Activity).
Do nothing on B, setResult() to something like "RESULT_FINISH_EVERYTHING", and when taking care of the results in A (override "onActivityResult()", IIRC), finish activity A also.
but i saw it's impossible to set different results depending on
selected RadioButton.
You could setResult() depending on thw button checked in the radio button. You can set listeners to the radio group and read which button is selected. See RadioGroup.OnCheckedChangeListener or View.OnClickListener if you need actions for eadch individual radio button.
Really, not too complicated. It just depends on what you want. I can clarify all this if you want.
Good luck!
I'm trying to completely replace a fragment back stack with one I generate based on some information returned via a network connection. I first pop the back stack to the position I want (that works fine... but lets say I pop to the root for simplicity), and then I try to build and apply a fragment stack like this:
ArrayList<JSONObject> crumbsOut = new ArrayList<JSONObject>(count);
//.... pop the back stack to a certain point
//replace entire nav. backstack
final FragmentTransaction transaction = this.getActivity().getSupportFragmentManager().beginTransaction();
for(int i = 0; i<count; i++)
{
final JSONObject item = crumbsOut.get(i);
final String id = item.getString("id");
FolderFragment currentFolder = new FolderFragment();//fragment displays folder contents
Bundle args = new Bundle();
args.putString(DATA_ITEM_ID_KEY, id);
args.putString(DATA_ITEM_NAME_KEY, item.getString("displayname"));
currentFolder.setArguments(args);
transaction.replace(R.id.MasterContainer, currentFolder);
transaction.addToBackStack(id);
}
// Commit the transaction
transaction.commit();
When I run this, the top-most FolderFragment is displayed properly, but when I hit the back button (or pop the stack), the view reverts to the point immediately before the above code is run (i.e. instead of going back in the stack of new fragments I created with the loop, I go back to the state just before trying to add/create this stack).
If it helps, I'm using the Android Compatibility Package in my project.
Please help. Thanks
I found the answer. You have to create unique transactions for each new fragment you want to add to your stack. I originally thought this wouldn't be necessary, but I guess this is not so. So, here is the answer:
ArrayList<JSONObject> crumbsOut = new ArrayList<JSONObject>(count);
//.... pop the back stack to a certain point
//replace entire nav. backstack
for(int i = 0; i<count; i++)
{
//move the transaction into the loop
final FragmentTransaction transaction = this.getActivity().getSupportFragmentManager().beginTransaction();
final JSONObject item = crumbsOut.get(i);
final String id = item.getString("id");
FolderFragment currentFolder = new FolderFragment();//fragment displays folder contents
Bundle args = new Bundle();
args.putString(DATA_ITEM_ID_KEY, id);
args.putString(DATA_ITEM_NAME_KEY, item.getString("displayname"));
currentFolder.setArguments(args);
transaction.replace(R.id.MasterContainer, currentFolder);
transaction.addToBackStack(id);
// Commit the transaction
//move the commit into the loop
transaction.commit();
}
could it be that you are doing everything in the same method and your beginTransaction() call is cancelling the pop (the FragmentManager no doubt begins a transaction to do the pop).-
I would suggest doing the clean up yourself using the same FragmentTransaction and only performing a single commit. Alternatively you could post your replacement calls into the main threads message queue so that it is performed 'later,.
As you're using the compat library you can always debug the source to see what's going on.