I'm facing the problem about removing Fragment in SDK < 24.
removeFragment()
FragmentTransaction frgTrans = fragmentMng.beginTransaction();
MyFragment myFrg = (MyFragment) fragmentMng.findFragmentByTag(TAG_MY_FRAGMENT);
frgTrans.remove(myFrg).commit();
getFragment()
MyFragment myFrg = (MyFragment) fragmentMng.findFragmentByTag(TAG_MY_FRAGMENT);
if (myFrg == null ) {
// Do sth
}
// But I checked that myFrg is NOT NULL ???
Furthermore, this problem only happened in SDK < 24 ( Android 5,6 ).
What's difference things between Android SDK < 24 and 24 above ?
I also try to call commitNow() for execute synchronously but it's same problem.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
frgTrans.commit();
fragmentMng.popBackStack();
} else {
frgTrans.commitNow();
}
Anyone here has same problem ?
Update:
I also check Fragment hashcode I get before add and before remove. It's same so I can affirm that it's existing...
05-12 11:34:38.705 3916-3916/myapp.test E/FragmentControllerTest: hashcode before remove: 136290746
05-12 11:34:39.856 3916-3916/myapp.test E/FragmentControllerTest: hashcode before add: 136290746
Update code
Calling follow:
GotoActivity 1: AddMyFragment()
GotoActivity 2: (destroy Activity1 ) removeMyFragment()
BackToActivity1: AddMyFragment() (onResume)
Code:
private void addMyFragment() {
MyFragment myFrg = (MyFragment) mActivity.getSupportFragmentManager().findFragmentByTag(TAG_MY_FRAGMENT);
if (myFrg == null) {
try {
myFrg = new MyFragment();
FragmentTransaction frgTrans = mActivity.getSupportFragmentManager().beginTransaction();
frgTrans.add(R.id.my_fragment, myFrg, TAG_MY_FRAGMENT);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
frgTrans.commit();
} else {
frgTrans.commitNow();
}
} catch (Exception e) {}
} else {
Log.e(TAG, "hash code after remove: " + myFrg.hashCode());
}
}
private void removeMyFragment() {
MyFragment myFrg = (MyFragment) mActivity.getSupportFragmentManager().findFragmentByTag(TAG_MY_FRAGMENT);
if (myFrg != null) {
Log.e(TAG, "hash code after add: " + myFrg.hashCode());
try {
FragmentTransaction frgTrans = mActivity.getSupportFragmentManager().beginTransaction();
frgTrans.remove(myFrg);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
frgTrans.commit();
mActivity.getSupportFragmentManager().popBackStack();
} else {
frgTrans.commitNow();
}
} catch (Exception e) {}
}
}
Related
I have two fragments in my activity. When I switch fragment use the code:
private void switchCourseFragment() {
if (mCourseFragment == null || getActivity().isFinishing()) return;
FragmentTransaction mTransaction = mFragmentManager.beginTransaction();
if (mFragmentLogin != null && mFragmentLogin.isAdded()) {
mTransaction.remove(mFragmentLogin);
}
if (!mCourseFragment.isAdded()) {
mTransaction.add(R.id.learncenter_contain, mCourseFragment).commitAllowingStateLoss();
}
}
But this is not what I expected. The mFragmentLogin is not been removed, but the mCourseFragment is been added.
You have to commit for it to take effect.
private void switchCourseFragment() {
if (mCourseFragment == null || getActivity().isFinishing()) return;
if (mFragmentLogin != null && mFragmentLogin.isAdded()) {
mFragmentManager.beginTransaction().remove(mFragmentLogin).commit();
}
if (!mCourseFragment.isAdded()) {
mFragmentManager.beginTransaction().add(R.id.learncenter_contain, mCourseFragment).commitAllowingStateLoss();
}
}
but why don't you use replace instead?
private void switchCourseFragment() {
if (mCourseFragment == null || getActivity().isFinishing()) return;
if (!mCourseFragment.isAdded()) {
mFragmentManager.beginTransaction().replace(R.id.learncenter_contain, mCourseFragment).commitAllowingStateLoss();
}
}
This assume that both your fragments are in the same container.
Any thoughts on why the fragment is not getting removed? This is an interface method being called from a 'AsyncTask' fragment with retaininstance(true). In the test of remove mTaskFragment is not equal to null so the log line prints.
#Override
public void onPostExecute(boolean success, String result, String message) {
Log.d(TAG, "onPostExecute()");
//Remove the Fragment if its still there
FragmentManager fm = getSupportFragmentManager();
mTaskFragment = (MyAsyncTaskFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT);
if (mTaskFragment != null) {
fm.beginTransaction().remove(mTaskFragment).commit();
}
mTaskFragment = null;
//Test of remove
mTaskFragment = (MyAsyncTaskFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT);
if (mTaskFragment != null) {
Log.d(TAG, "mTaskFragment is not null");
}
I'm having an issue restarting the fragment in my Activity. When the user completes a round, I need the fragment that is implemented by the activity to completely restart. The fragment has an if / else statement that checks to see if a round has already been played, and if so, it needs to reload the fragment with alternative values. I've been looking all over and can't seem to find much of anything on this, so any help is much appreciated!
public class BoardActivity extends Activity implements BoardFragment.OnFragmentInteractionListener {
public void gameTimer() {
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView tv = (TextView) findViewById(R.id.RoundTimer);
tv.setText(String.valueOf(minutes) + ":" + String.valueOf(seconds) + ":" + String.valueOf(milliseconds));
milliseconds -= 1;
if (milliseconds == 0) {
tv.setText(String.valueOf(minutes) + ":" + String.valueOf(seconds) + ":" + String.valueOf(milliseconds));
milliseconds = 1000;
seconds = seconds - 1;
}
if (seconds == 0) {
timerout = "Out of Time!";
tv.setText(String.valueOf(timerout));
}
if (timerout == "Out of Time!") {
Model.gameCounter = Model.gameCounter + 1;
nextRound();
}
}
});
}
}, 0, 1);
}
private void nextRound(){
//redirect to next round
if (Model.gameCounter <= 1 && Model.gameCounter < 4) {
Log.d(TAG,"new round");
Fragment frg = null;
frg = getFragmentManager().findFragmentByTag("BoardFragment");
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.detach(frg);
ft.attach(frg);
ft.commit();
} else {
//eventually add score screen
Log.d(TAG,"same round");
}
}
The documentation on attach and detach does say that the view hierarchy get destroyed and rebuilt. However, the state of the views is maintained, and so it should be expected to seem as if nothing has happened (which it sounds like what you are seeing). The docs say it is the same thing that happens when the fragment goes on the back stack.
Thus I think you have two options for "restarting" the fragment.
Option 1: Create a new fragment each time.
private void nextRound(){
//redirect to next round
if (Model.gameCounter <= 1 && Model.gameCounter < 4) {
Log.d(TAG,"new round");
Fragment frg = null;
frg = getFragmentManager().findFragmentByTag("BoardFragment");
Fragment newfrg = new BoardFragment();
final FragmentTransaction ft = getFragmentManager().beginTransaction();
if (frg == null) {
ft.add(R.id.board_container, newfrg, "BoardFragment");
} else {
ft.replace(R.id.board_container, newfrg, "BoardFragment");
}
ft.commit();
} else {
//eventually add score screen
Log.d(TAG,"same round");
}
}
Option 2: Write a method in BoardFragment to reset the board for the next round, and call that when you are restarting.
private void nextRound(){
//redirect to next round
if (Model.gameCounter <= 1 && Model.gameCounter < 4) {
Log.d(TAG,"new round");
BoardFragment frg = (BoardFragment)
getFragmentManager().findFragmentByTag("BoardFragment");
frg.restart();
} else {
//eventually add score screen
Log.d(TAG,"same round");
}
}
I have a spinner item.When I click on position 2. I want the following code to execute.
if (pos == 2) {
Log.e("TEST", "YOU CLICKED" + pos);
for (int i = 0; i < 2; i++) {
Log.e("TEST INSIDE FOR", "YOU CLICKED" + pos);
if (i % 2 == 0) {
// showFragOne();
showFragOne();
Log.e("TEST INSIDE FOR " + i, "YOU CLICKED" + pos);
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (i %2 == 1) {
// showFragTwo();
showFragTwo();
Log.e("TEST INSIDE FOR " + i, "YOU CLICKED" + pos);
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Toast.makeText(mCoreContext,"One Fragment added from feature app",Toast.LENGTH_LONG).show();
}
What i want this code to do is change the Frame layout in 20s. But only the second fragment I am getting at last. Here showFragTwo() function:-
private void showFragTwo() {
// TODO Auto-generated method stub
FragmentTransaction ft =
.getFragmentManager().beginTransaction();
if (fragtwo.isAdded()) {
if (fragtwo.isHidden())
ft.show(fragtwo);
else
ft.hide(fragtwo);
ft.commit();
} else {
ft.replace(com.example.coreapp.R.id.container, fragtwo,
"ADDFRAGTWO").commit();
ft.show(fragtwo);
}
}
Here showFragOne() function:-
private void showFragOne() {
// TODO Auto-generated method stub
FragmentTransaction ft =
.getFragmentManager().beginTransaction();
if (frag.isAdded()) {
if (frag.isHidden())
ft.show(frag);
else
ft.hide(frag);
ft.commit();
} else {
ft.replace(com.example.coreapp.R.id.container, frag, "ADDFRAG")
.commit();
ft.show(frag);
}
}
where i initialize frag and fragtwo at top to following to fragment class :-
frag = new AddFragmentOne();
fragtwo = new AddFragmentTwo();
I get the following as logcat o/p.
**02-28 04:56:34.250: E/TEST INSIDE FOR: YOU CLICKED2
02-28 04:56:34.290: E/TEST INSIDE FOR 0: YOU CLICKED2
02-28 04:56:54.350: E/TEST INSIDE FOR: YOU CLICKED2
02-28 04:56:54.350: E/TEST INSIDE FOR 1: YOU CLICKED2**
I am getting the logs message in o/p BUT WHY THE FRAGMENT IS NOT GETTING DISPLAYED?
ONLY THE SECOND FRAGMENT I AM GETTING
If i comment second if statemnt I AM GETTING FIRST FRAGMENT
Can anybody tell me where i am getting wrong?
This happens because you make the Thread sleep 20 seconds and its likely the Ui Thread.
You should replace this Thread.sleep() statement by a Timer that shows the appropriate fragment :
if(pos == 2) {
showFragOne();
new Timer().schedule(new TimerTask() {
#Override
public void run() {
showFragTwo();
}
}, 20000);
}
To complete the answer based on commentaries : I suggest you make one method
/** This method shows the fragment whose 'number' is {#code fragmentNumber}. */
public void showFrag(int fragmentNumber) {
Fragment f = fragments.get(fragmentNumber); // fragments can be a List for example
// Then use the code you already have to show fragment 'f'
}
Then, you can modify the preceding code like this :
if(pos == 2) {
final AtomicInteger count = new AtomicInteger(0); // Because use inside TimerTask requires a final variable
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if(count.get() < MAX_FRAG_INDEX)
showFrag(count.getAndIncrement);
else
cancel();
}
}, 0, 2000);
}
I need a little help to get an idea of how to refactor my code, but I can't see options besides what's done, I would like to add the objects but not using so many lists (and if's conditions).
Here is my code, if anyone could help, I appreciate. Thanks
#ViewById
BannerHomeViewPager place1, place2, place3, place4, place5, place6, place7,
place8, place9;
The lists:
private List<HomeItem> allHomeItems = new ArrayList<HomeItem>(),
placeItems1 = new ArrayList<HomeItem>(),
placeItems2 = new ArrayList<HomeItem>(),
placeItems3 = new ArrayList<HomeItem>(),
placeItems4 = new ArrayList<HomeItem>(),
placeItems5 = new ArrayList<HomeItem>(),
placeItems6 = new ArrayList<HomeItem>(),
placeItems7 = new ArrayList<HomeItem>(),
placeItems8 = new ArrayList<HomeItem>(),
placeItems9 = new ArrayList<HomeItem>();
1) Items mocked, ok.
2)
#UiThread
void updateUI() {
if (allHomeItems != null && allHomeItems.size() > 0) {
for (HomeItem item : allHomeItems) {
if (item.getPlacement().contains("1")) {
placeItems1.add(item);
} else if (item.getPlacement().contains("2")) {
placeItems2.add(item);
} else if (item.getPlacement().contains("3")) {
placeItems3.add(item);
} else if (item.getPlacement().contains("4")) {
placeItems4.add(item);
} else if (item.getPlacement().contains("5")) {
placeItems5.add(item);
} else if (item.getPlacement().contains("6")) {
placeItems6.add(item);
} else if (item.getPlacement().contains("7")) {
placeItems7.add(item);
} else if (item.getPlacement().contains("8")) {
placeItems8.add(item);
} else {
placeItems9.add(item);
}
}
}
setupAdapters();
}
3) setupAdapters()
private void setupAdapters() {
if (place1 != null)
place1.update(placeItems1);
if (place2 != null)
place2.update(placeItems2);
if (place3 != null)
place3.update(placeItems3);
if (place4 != null)
place4.update(placeItems4);
if (place5 != null)
place5.update(placeItems5);
if (place6 != null)
place6.update(placeItems6);
if (place7 != null)
place7.update(placeItems7);
if (place8 != null)
place8.update(placeItems8);
if (place9 != null)
place9.update(placeItems9);
}
As #DanielBo answer:
private Map<String, ArrayList<HomeItem>> placeItems = new HashMap<String,ArrayList<HomeItem>>();
void updateUI() {
if (allHomeItems != null && allHomeItems.size() > 0) {
for (HomeItem item : allHomeItems) {
if(!placeItems.containsKey(item.getPlacement())){
placeItems.put(item.getPlacement(), new ArrayList<HomeItem>());
}
placeItems.get(item.getPlacement()).add(item);
}
}
setupAdapters();
}
But why are you using so many views? I really can't say if this is correct bcs i don't know the purpose of this, but I can't figure a good use to so many list views in the same layout...