I'm trying to make an Activity Transition using Shared Elements on a pre-Lollipop device (4.x). Is it possible? So far, I'm trying this:
public class RewardDetail extends ActionBarActivity {
#Override
public void onCreate(final Bundle savedInstanceState) {
...
ViewCompat.setTransitionName(imageView, TRANSITION_NAME);
}
...
public static void launch(ActionBarActivity activity, View transitionView, WelcomeReward detailData) {
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, transitionView, TRANSITION_NAME);
Intent intent = new Intent(activity, RewardDetail.class);
intent.putExtra(PARAM_DATA, detailData);
ActivityCompat.startActivity(activity, intent, options.toBundle());
}
}
called by:
#Override
public void onClick(final View v) {
int position = recyclerView.getChildPosition(v);
WelcomeReward welcomeReward = data.get(position);
RewardDetail.launch(WelcomeRewardActivity.this, v.findViewById(R.id.reward_view), welcomeReward);
}
But it results in a "regular" transition (no shared element). Any ideas?
EDIT
According to this video, it could be done:
https://www.youtube.com/watch?v=RhiPJByIMrM&index=8&list=WL
Is there a library already implementing this for pre Lollipop ?
No, Activity/Fragment Transitions are not possible on pre-Lollipop devices. According to the documentation:
Start an activity with additional launch information, if able.
In Android 4.1+ additional options were introduced to allow for more control on activity launch animations. Applications can use this method along with ActivityOptionsCompat to use these animations when available. When run on versions of the platform where this feature does not exist the activity will be launched normally.
See also George Mount's answer to this StackOverflow question.
You can check out this library for activity and fragment transitions for pre lollipop devices
dependencies {
compile 'com.albinmathew:PreLollipopTransition:1.1.2'
}
https://github.com/albinmathew/PreLollipopTransition
Although the fancy Lollipop Activity/Fragment transitions are not available pre-Lollipop (without the use of a 3rd party library), you can still override the animation used to transition between activities.
Just before/after you start invoke startActivity() you can make a call to [Activity.overridePendingTransition](http://developer.android.com/reference/android/app/Activity.html#overridePendingTransition(int, int)). When you leave your activity, call the same method.
Similarly you can use ActivityOptionsCompat to define a custom animation to use during a transition.
ActivityOptionsCompat opts =
ActivityOptionsCompat.makeCustomAnimation(getActivity(), R.anim.in, R.anim.out);
startActivity(intent, opts.toBundle());
There is a support library, but it does not support (all) transitions on Android versions below 5.0. There are however some alternatives:
Unofficial Compatibility libraries https://github.com/andkulikov/transitions-everywhere
https://github.com/takahirom/PreLollipopTransition
https://github.com/lgvalle/Material-Animations
Android KitKat http://www.doubleencore.com/2013/11/new-transitions-framework/ and a
sample found in your SDK samples folder.
Posted earlier to a duplicate of this question here: https://stackoverflow.com/a/27344471/1683141
Related
I work on a very large project. It has a lot of modules and views - activities and fragments. I need to understand what fragment and what activity are running at the moment. Then I need to move to this class of activity and fragment in the project. Is there any way to find the class name? maybe by logs or smth else?
You can use Android Profiler for this. You will also get a lot of details from this tool. Just hover the mouse over the graph, it will show the fragment currently showing.
You can try Layout Inspector, a feature of android studio. When the app is running on a device that is connected to Android Studio, you can click on any item to see the view id, you can use these ids to find out which activity/fragment they belong to (through xml file)
Use UserVisibleHint for that:
boolean isVisible;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
isVisible = isVisibleToUser;
}
When you want to check the visibility of the fragment, simply check it.
if (isVisible) {
//Fragment is visible
} else {
//Fragment is not visible
}
BaseActivity.java
Language switching
Intent
OnClickListener
Error demo
No error message was prompted.
You are using old style of language switching which were deprecated and removed after releasing android 8, It completely get removed. However, if you want to use your application for android prior to 26, you have to change your code on language switching Image as follow:
Configuration config = new Configuration(resources.getConfiguration());
New instance of Configuration have to be passed to resources.updateConfiguration();. above line will solve your problem.
On the other hand if you want to publish your application for higher api level, follow link below:
#Override
protected void attachBaseContext(Context newBase){
Configuration configuration = new Configuration(newBase.getResoureces().getConfiguration());
configuration.setLocale(/* selected locale which you have to get from user or your app configuration*/);
super.attachBaseContext(newBase.createConfigurationContext(configuration));
}
Override above method for each activity which you have declared;
I'm trying to get an app to show all the users online... I'm using the following way to achieve it
#Override
public void onStart(){
super.onStart();
mDatabaseReference.child("Online").setValue(true);
}
#Override
public void onStop(){
super.onStop();
mDatabaseReference.child("Online").setValue(false);
}
I'm not using ondisconnect because it shows offline only if the app is completely closed(not running in the background). SO i used this method on each activity... But the problem is that whenever i open an activity it shows online and the next second turns offline... I'm guessing that its because the prev activity closes after opening the new activity so the presents activity's on start is executed before the next activity's on stop. So since the activity's onstop is executed last it shows offline. How do i solve this problem
Your guess is completely correct.
Starting a new Activity will cause the onPause() and onStop() methods to be called for the first Activity.
Based on your description, I'm assuming that you want the Online status to remain true for as long as the app remains in the foreground and you are putting it in every Activity because your app doesn't only open from a single main Activity.
Your current code will work without a problem if you switched to using a Single-Activity Architecture, which is simply to use a single Activity and have it display different Fragments instead of new Activities. This solution will work with your existing code because onStop() and onStart() will only be called when your app enters the background.
If you look at the Navigation section in the official Android Developers Blog, you'll see that Google wants to encourage developers to switch to using the Single-Activity Architecture.
However, if you still wish to use multiple Activities, then you might want to consider an alternative way to keep track of Online status to take into account of multiple Activities.
For example, rather than using a simple boolean value, you can use an int value.
#Override
public void onStart(){
super.onStart();
mDatabaseReference.child("Online").addValue();
}
#Override
public void onStop(){
super.onStop();
mDatabaseReference.child("Online").removeValue();
}
With the addValue() and removeValue() being:
private void addValue(){
activityCount++;
onlineStatus = true;
}
private void removeValue(){
activityCount--;
if(activityCount <= 0)
onlineStatus = false;
}
Please keep in mind that this is only an example and doesn't take into account of how your app is designed. Bottom line is, you'll have to think of a solution that takes into account of multiple Activities that are displayed.
I do heavily suggest the Single-Activity approach.
I have migrated my application to Android O in Android Studio 3
Running on an Android O emulator all my dialogFragments now fail with :-
java.lang.IllegalStateException: Fragment MyDialogFragment{43ccf50 #2 MyDialogFragment} declared target fragment SettingsFragment{ceed549 #0 id=0x7f0f0142 android:switcher:2131689794:0} that does not belong to this FragmentManager!
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1316)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1624)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1689)
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:794)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2470)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2260)
at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(FragmentManager.java:2213)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2122)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:746)
at android.os.Handler.handleCallback(Handler.java:769)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6535)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
I have made no code changes whatsoever.
What has changed in Android O that previously working DialogFragments now fail display?
Android Studio 3.0 Canary 1
Build #AI-171.4010489, built on May 15, 2017
JRE: 1.8.0_112-release-b736 x86_64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Mac OS X 10.11.6
compileSdkVersion 'android-O'
buildToolsVersion "26.0.0-rc2"
AndroidManifest.xml
defaultConfig {
minSdkVersion 16
targetSdkVersion 'O'
}
compile 'com.android.support:appcompat-v7:26.0.0-beta1'
compile 'com.android.support:cardview-v7:26.0.0-beta1'
compile 'com.android.support:design:26.0.0-beta1'
compile 'com.android.support:percent:26.0.0-beta1'
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-alpha1'
}
For me this was not only an issue on Android O but also on older versions.
The oldest version I tested was API Level 16.
I was instantiating my fragments using this code:
MyFragment myFragment = MyFragment.newInstance();
myFragment.setTargetFragment(ParentFragment.this, 0);
myFragment.show(getActivity().getSupportFragmentManager(), null);
Where ParentFragment.this is a custom class extending android.support.v4.app.Fragment, MyFragment also extends this class and is a child fragment of the ParentFragment fragment (hence it's name).
I thought that I had to use a SupportFragmentManager (the getSupportFragmentManager() method) because I am using a fragment of the support package so I tried to call getActivity().getSupportFragmentManager() to get an activity reference that supported this method.
This does not seem to be the correct way though.
I changed those calls to:
MyFragment myFragment = MyFragment.newInstance();
myFragment.setTargetFragment(ParentFragment.this, 0);
myFragment.show(getFragmentManager(), null);
so the fragment decides on it's own which FragmentManager to use and the error is gone now.
Hope this helps someone.
I had the same problem, definitely an android bug. It happens when you are showing a fragment from another fragment using it as target. As workaround you can use:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
getActivity().getFragmentManager().beginTransaction().add(dialogFrag, "dialog").commit();
else
getChildFragmentManager().beginTransaction().add(dialogFrag,"dialog").commit();
I just faced the same issues in the project that I am currently working on when we moved to Android Studio 3 and upgraded the support library to version 26. All of a sudden, without changing the code, we got tons of this exception. In the end I found out the following:
Google added a new "sanity check" in the sources of the v4 Fragment Manager in January this year (not sure into what release that went) that refuses to add a fragment using a Fragment Manager, if it has a target fragment set and the target fragment cannot be found in the active fragments of the same Fragment Manager. The patch is described in detail here
Ealier versions seem to not have cared about that. Took me a few days to update all the areas in our code where fragments that were added using the Support Fragment Manager used the Child Fragment Manager for their subfragments with the parent fragment as target. Well, late penalty for writing bad code.
I had the same case as Markus Ressel but I was using getChildFragmentManager(). I replaced that with getFragmentManager() and it resolved the issue.
UPDATE: I've now been working with childFragmentManager and have some feedback.
When dealing with inner fragments that are hosted by a fragment (so a fragment within a fragment) use the childFragmentManager. This fragment manager differs from the activities getSupportFragmentManager. The two are not the same. It's a separation of concerns.
So I've made a rule that fragments hosting child fragments will always use the childFragmentManager and things not inside host fragments can use getSupportfragmentManager.
Recently, my app experienced the same issue when I targeted it for Android O. As a solution, use:
myDialogFragment.show(SettingsFragment.this.getFragmentManager(), TAG);
instead of:
myDialogFragment.show(getFragmentManager(), TAG);
// or
myDialogFragment.show(getSupportFragmentManager(), TAG);
// or
myDialogFragment.show(getChildFragmentManager(), TAG);
while working on an app, I encountered the same problem and I solved it by (in kotlin)
chooseRegionFragment.setTargetFragment(this#ParentFragment, REQUEST_CODE_CHOOSE_REGION)
chooseRegionFragment.show(this#ParentFragment.parentFragmentManager, TAG)
it translates to
chooseRegionFragment.setTargetFragment(ParentFragment.this, REQUEST_CODE_CHOOSE_REGION);
chooseRegionFragment.show(ParentFragment.this.getParentFragmentManager, TAG);
in java
my app work well until upgrade target version to 27 then i face same issue when call setTargetFragment (Fragment fragment,
int requestCode)
example:
chooseRegionFragment.setTargetFragment(ParentFragment.this, REQUEST_CODE_CHOOSE_REGION);
just change to:
chooseRegionFragment.setTargetFragment(getRootParentFragment(this), REQUEST_CODE_CHOOSE_REGION);
getRootParentFragment(this) this method will find root parent of fragments for you
/**
* find root parent of fragment
*/
public static Fragment getRootParentFragment(Fragment fragment) {
Fragment parent = fragment.getParentFragment();
if(parent == null)
return fragment;
else
return getRootParentFragment(parent);
}
Use below solution and you do not need to worry about which fragment managers you are dealing with,
Assuming that you must have used a BaseFragment.
First create an interface:
public interface OnRequestUpdateListener {
void onRequestUpdate(final int requestCode, final Intent data);
void setRequestFragment(final BaseFragment requestFragment, int requestCode);
BaseFragment getRequestFragment();
int getRequestCode();
}
Implement that interface in your BaseFragment
public class BaseFragment extends Fragment implements OnRequestUpdateListener {
private BaseFragment requestFragment;
private int requestCode;
#Override
public void onRequestUpdate(int requestCode, Intent data) {
// you can implement your logic the same way you do in onActivityResult
}
#Override
public void setRequestFragment(BaseFragment requestFragment, int requestCode) {
this.requestFragment = requestFragment;
this.requestCode = requestCode;
}
#Override
public BaseFragment getRequestFragment() {
return requestFragment;
}
#Override
public int getRequestCode() {
return requestCode;
}
}
Then, replace the setTargetFragment with setRequestFragment and replace getTargetFragment with getRequestFragment.
Here, you could also user onRequestUpdate in place of onActivityResult.
This is a custom solution without bothering about the which fragment manager you are using.
Using getFragmentManager() instead of getChildFragmentManager() would also work but it affects getParentFragment(). If you do not use a getChildFragmentManager() for nested fragment then you will not be able the get the parent fragment by using getParentFragment() in child/nested fragment.
In my first steps in exploring Android I now start with QR scanning.
Works all pretty well. But I am not able to come back from the ResultHandler after read the QR successfully to my MainActivity.
public class MainActivity extends AppCompatActivity implements
ZXingScannerView.ResultHandler
{
private ZXingScannerView mScannerView
....
#Override
public void handleResult(Result rawResult)
{
// my results are ok in rawResult
// the scanner does not scan anymore but it is still there
// how to go back to my main activity???
}
public void ClickButton (View view)
{
mScannerView = new ZXingScannerView(this);
setContentView(mScannerView);
mScannerView.setResultHandler(this);
mScannerView.startCamera();
}
}
}
I tried
mScannerview.stopCameraPreview
mScannerView.stopCamera
this.finish
setContentView(R.layout.activity_main); // shows my activity_main
// but I can not click anything
Thanks!!
EDIT
I added some code to describe it a bit better. The idea is from
https://www.numetriclabz.com/android-qr-code-scanner-using-zxingscanner-library-tutorial/
Your question isn't clear but I'm assuming you want to restart the scan process. Normally, you'd have to restart the SurfaceHolder to be in preview mode. Luckily for you the ZXingScannerView already has a method to do that. Call mScannerView.resumeCameraPreview(this) to restart the scan process.
Otherwise can you clarify? You say you want to go back but you're already in MainActivity
If you want to go back into activities/fragments stack you can try Activity.onBackPressed()
if you are in a fragment you must call this method against attached Activity
What do you want is not going back to your activity. You want to restore activity's layout.
I think the better choice is to add ScannerView to your activity's layout file with android:visibility="gone". Then in on click you can get this view and change it's visibility to VISIBILE.
Then when you have handled scanning result, you can reset yuoir ScannerView to visibility = GONE
I too was stuck with this problem for an hour, just like you. And later realised..
To solve this problem DON NOT implementing the ZXingScannerView in the same activity or fragment. Instead start a new activity when you click the button and this activity is just for the ZXingScannerView
Once the Scan is done finish and pass the data back to your activity or fragment
Just restart your MainActivity before this.finish()
the code below will start your main activity through intent...
worked fine for me
startActivity(new Intent(this,MainActivity.class));
this.finish();
remove from onCreate method this line setContentView(your layout) and when you finish scan write it after you stoped the camera then you can use your layout after scan
I had a bit of a look into android concepts and activities.
I put the QR handling in a 2nd activity and it worked well with the finish ().
Thanks for help anyway!!
I think it's too late, but I came with the same problem and I had so find a solution by myself.
You were on the right way, you need two steps more.
I called the methods where I link and set the listener of any buttons
There are the methods
Basically you were right where you set the content view, but you need to give the buttons their functionality back.
(I know its late, but better late than never). Good luck!