Using the Navigation framework, I navigate from a Homefragment to a WhsSearchWebDB and back to the Homefragment.
When coming back via navController.navigate(R.id.action_whsSearchWebDB_to_nav_home) or navController.navigateUp(), the screen is completely white and the toolbar is only partially updated: the name of the fragment is correctly set, but the navigation button shows a back arrow instead of the three-bar-home icon.
When coming back via the back button (navController.navigate(R.id.action_whsSearchWebDB_to_nav_home) and navController.navigateUp() commented, of course), everything works fine.
I need to come back programmatically, not through a user click on the back button. What must I change?
mobile_navigation.xml:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mobile_navigation"
app:startDestination="#id/nav_home">
<fragment
android:id="#+id/nav_home"
android:name="be.ema.golfclubdataconversion.ui.home.HomeFragment"
android:label="#string/menu_home"
tools:layout="#layout/fragment_home" >
<action
android:id="#+id/action_nav_home_to_whsSearchWebDB"
app:destination="#id/whsSearchWebDB" />
</fragment>
<fragment
android:id="#+id/nav_gallery"
android:name="be.ema.golfclubdataconversion.ui.gallery.GalleryFragment"
android:label="#string/menu_gallery"
tools:layout="#layout/fragment_gallery" />
<fragment
android:id="#+id/nav_slideshow"
android:name="be.ema.golfclubdataconversion.ui.slideshow.SlideshowFragment"
android:label="#string/menu_slideshow"
tools:layout="#layout/fragment_slideshow" />
<fragment
android:id="#+id/whsSearchWebDB"
android:name="be.ema.golfclubdataconversion.ui.WhsSearchWebDB"
android:label="WhsSearchWebDB"
tools:layout="#layout/search_web_db">
<action
android:id="#+id/action_whsSearchWebDB_to_nav_home"
app:destination="#id/nav_home" />
</fragment>
home_fragment code:
public class HomeFragment extends Fragment {
public static View root = null;
private HomeViewModel homeViewModel;
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_home, container, false);
}
#Override
public void onViewCreated(View root, Bundle savedInstanceState) {
// super.onViewCreated(root, savedInstanceState);
// homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
Button openUrlBtn = root.findViewById(R.id.openUrlBtn);
openUrlBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
NavController navController = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
navController.navigate(R.id.action_nav_home_to_whsSearchWebDB);
}
});
}
}
WhsSearchWebDB code :
public class WhsSearchWebDB extends Fragment {
public static Activity activity;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.search_web_db, container, false);
return v;
}
#Override
public void onViewCreated(View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
activity = getActivity();
String urlToBeOpened = "http://ncrdb.usga.org";
WebView webView = (WebView) root.findViewById(R.id.webView1);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new MyJavaScriptInterface(), "HTMLOUT");
webView.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url)
{
/* This call inject JavaScript into the page which just finished loading. */
webView.loadUrl("javascript:window.HTMLOUT.processHTML('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');");
}
});
webView.loadUrl(urlToBeOpened);
}
public static class MyJavaScriptInterface {
#JavascriptInterface
public void processHTML(String html) {
NavController navController = Navigation.findNavController(activity, R.id.nav_host_fragment);
// navController.navigate(R.id.action_whsSearchWebDB_to_nav_home);
// if (!navController.navigateUp()) {
// System.out.println("nooooooo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
// }
}
}
}
**** EDIT ****
This is the expected screen (picture taken before going to the WhsSearchWebDB fragment:
And this is the incorrect result after trying to come back:
The Navigation Action in the whsSearchWebDB Fragment is creating a new fragment not popping back to the original fragment.
<fragment
android:id="#+id/whsSearchWebDB"
android:name="be.ema.golfclubdataconversion.ui.WhsSearchWebDB"
android:label="WhsSearchWebDB"
tools:layout="#layout/search_web_db">
<action
android:id="#+id/action_whsSearchWebDB_to_nav_home"
// Change this
app:destination="#id/nav_home"
// To this to pop back
app:popUpTo="#id/nav_home" />
I didn't include popUpToInclusive.
PopUPToInclusive needs to be set to true, the popUpTo attribute will remove all destinations up to and including the given destination from the back stack.
Open the navigation graph
Select the action, you want
In the attributes pane, set popUptToInclusive the homeFragment, Select the popUpToInclusive check box to true.
For more details please check out the Navigation code lab.
https://developer.android.com/codelabs/android-navigation#6
I finally discovered that the processHTML method executes on a background thread, hence not allowed to manipulate UI widgets. I had preferred a crash/error instead of the unexpected behavior I have reported in my question.
I have fixed the issue by implementing a ViewModel, selecting the result in the processHTML method and observing it on the UI thread as follows:
#Override
public void onViewCreated(View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
activity = getActivity();
String urlToBeOpened = "http://ncrdb.usga.org";
WebView webView = (WebView) root.findViewById(R.id.webView1);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new MyJavaScriptInterface(), "HTMLOUT");
webView.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url)
{
/* This call inject JavaScript into the page which just finished loading. */
webView.loadUrl("javascript:window.HTMLOUT.processHTML('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');");
}
});
webView.loadUrl(urlToBeOpened);
TeeboxViewModel teeboxViewModel = new ViewModelProvider((ViewModelStoreOwner) activity).get(TeeboxViewModel.class);
teeboxViewModel.getSelected().observe(getViewLifecycleOwner(), item -> {
...
NavController navController = Navigation.findNavController(activity, R.id.nav_host_fragment);
navController.navigate(R.id.action_whsSearchWebDB_to_nav_newResult);
});
}
public static class MyJavaScriptInterface {
public static Map<String, String> coursesList = new HashMap<>();
#JavascriptInterface
public void processHTML(String html) {
...
TeeboxViewModel model = new ViewModelProvider((ViewModelStoreOwner) activity).get(TeeboxViewModel.class);
model.select(teeboxesList);
}
}
Related
So I have a problem with a fragment not showing inside a MvxCachingFragmentCompatActivity.
The pattern I use to get to the problem is as follows:
Register a activity.
Navigate to another activity that extends MvxCachingFragmentCompatActivity
Load the fragment using await _navigationService.Navigate<[TheFragmentViewModel]>();
Fragment loading is called but it doesn't show anything.
Fragment declaration:
[MvxFragment(typeof(MainViewModel), Resource.Id.content_frame, true)]
[Register(nameof(FirstFragment))]
public class FirstFragment : MvxFragment<FirstViewModel>
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var view = this.BindingInflate(Resource.Layout.FirstView, container, false);
return view;
}
}
Main activity: (nothing special I think)
[Activity(Label = "Fragment View")]
public class MainActivity : MvxCachingFragmentCompatActivity<MainViewModel>
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.MainView);
}
}
Main viewmodel
public class MainViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public MainViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
Init();
}
public async void Init()
{
await _navigationService.Navigate<FirstViewModel>();
}
}
Main activity layout: (very simple layout)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
I also added a sample on github: Github link.
I also added a bug report on the Mvvmcross github, but I am not sure if it is a bug on my part or theirs?
You should never use async void or start an async task from a non async command. These are the first problems. Also your Initialize is not called because you are not using RegisterNavigationServiceAppStart<>(). Another thing is that you are supposed to navigate directly to a fragment and not first to the activity, because MvvmCross will handle that.
Another hint will be to use Dependency injection to resolve IMvxNavigationService.
Following is my scenario.
I have an Activity MainActivity which has one FAB. When the user clicks on FAB, I open a full screen DialogFragment. I want to open the DialogFragment with some transitions.
Here is the code that I have tried so far.
//MainActivity.java
final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ReviewDialog reviewDialog = ReviewDialog.newInstance();
Slide slide = new Slide();
slide.setSlideEdge(Gravity.LEFT);
slide.setDuration(1000);
reviewDialog.setEnterTransition(slide);
Bundle bundle =ActivityOptions.makeSceneTransitionAnimation(ScrollingActivity.this)
.toBundle();
reviewDialog.setArguments(bundle);
reviewDialog.show(getSupportFragmentManager(),"review");
}
});
And here is the code of the DialogFragment ReviewDialog.
//ReviewDialog.java
public class ReviewDialog extends DialogFragment {
static ReviewDialog newInstance() {
ReviewDialog f = new ReviewDialog();
// Supply num input as an argument.
Bundle args = new Bundle();
f.setArguments(args);
return f;
}
#Override
public void onActivityCreated(Bundle arg0) {
super.onActivityCreated(arg0);
Slide slide = new Slide();
slide.setSlideEdge(Gravity.LEFT);
slide.setDuration(1000);
getDialog().getWindow().setEnterTransition(slide);
getDialog().getWindow().setExitTransition(slide);
getDialog().getWindow().setReenterTransition(slide);}
#Override
public void onCreate(Bundle bundle){
super.onCreate(bundle);
setStyle(DialogFragment.STYLE_NORMAL,R.style.DialogTheme);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog_review, container, false);
return v;
}
}
Also I have set the following property in my AndroidManifest.xml
<item name="android:windowContentTransitions">true</item>
The problem is when the ReviewDialog is started, it doesn't show any transitions.
I am able to show transitions between different activities but finding it very hard to show transition between Activity and Fragment.
How to show transitions when a DialogFragment is started ?
Use this library : https://github.com/lgvalle/Material-Animations
Shared element transition works with Fragments in a very similar way as it does with activities.
a) Enable Window Content Transition
values/styles.xml
<style name="MaterialAnimations" parent="#style/Theme.AppCompat.Light.NoActionBar">
...
<item name="android:windowContentTransitions">true</item>
...
</style>
b) Define a common transition name
layout/fragment_a.xml
<ImageView
android:id="#+id/small_blue_icon"
style="#style/MaterialAnimations.Icon.Small"
android:src="#drawable/circle"
android:transitionName="#string/blue_name" />
layout/fragment_b.xml
<ImageView
android:id="#+id/big_blue_icon"
style="#style/MaterialAnimations.Icon.Big"
android:src="#drawable/circle"
android:transitionName="#string/blue_name" />
c) Start a fragment with a shared element
FragmentB fragmentB = FragmentB.newInstance(sample);
// Defines enter transition for all fragment views
Slide slideTransition = new Slide(Gravity.RIGHT);
slideTransition.setDuration(1000);
sharedElementFragment2.setEnterTransition(slideTransition);
// Defines enter transition only for shared element
ChangeBounds changeBoundsTransition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds);
fragmentB.setSharedElementEnterTransition(changeBoundsTransition);
getFragmentManager().beginTransaction()
.replace(R.id.content, fragmentB)
.addSharedElement(blueView, getString(R.string.blue_name))
.commit();
And this is the final result:
I am trying to implement transitions between fragments which have "shared elements" as described in the new material design specs.
The only method I can find is the ActivityOptionsCompat.makeSceneTransitionAnimation, which I believe works on Activity only.
I've been searching for this same functionality but with/for fragments.
I had the same problem but had it working by adding a new fragment from another fragment.
The following link is very helpful in getting started on this: https://developer.android.com/training/material/animations.html#Transitions
Following is my code that works. I'm animating an ImageView from one fragment to the other.
Make sure the View you want to animate has the same android:transitionName in both fragments.
The other content doesn't really matter.
As a test, you could copy this to both your layout xml files. Make sure the image exists.
<ImageView
android:transitionName="MyTransition"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="#drawable/test_image" />
Then I have 1 file in my res/transition folder, named change_image_transform.xml.
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeImageTransform />
</transitionSet>
Now you can get started. Lets say you have Fragment A containing the image and want to add Fragment B.
Run this in Fragment A:
#Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.product_detail_image_click_area:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
setExitTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.explode));
// Create new fragment to add (Fragment B)
Fragment fragment = new ImageFragment();
fragment.setSharedElementEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
fragment.setEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.explode));
// Our shared element (in Fragment A)
mProductImage = (ImageView) mLayout.findViewById(R.id.product_detail_image);
// Add Fragment B
FragmentTransaction ft = getFragmentManager().beginTransaction()
.replace(R.id.container, fragment)
.addToBackStack("transaction")
.addSharedElement(mProductImage, "MyTransition");
ft.commit();
}
else {
// Code to run on older devices
}
break;
}
}
The shared element fragment transitions do work with ListViews, as long as the source and target views have the same (and unique) transitionName.
If you make your list view adapter to set unique transitionNames to the views you want (e.g. some constant + specific item id) and also change your detail fragment to set the same transitionNames to the target views at runtime (onCreateView), the transitions actually work!
Shared elements do work with Fragments but there are some things to keep in mind:
Don't try to set the sharedElementsTransition in the onCreateView of your Fragment. You have to define them when creating an instance of your Fragment or in onCreate.
Take note of the official documentation on the possible animations for enter/exit transitions & sharedElementTransition. They are not the same.
Trial and error :)
This should be a comment to the accepted answer, as I am unable to comment on it.
The accepted answer (by WindsurferOak and ar34z) works, except for a "minor" problem which caused a null pointer exception when navigating up with the backStack. It seems that setSharedElementReturnTransition() should be called on the target fragment instead of the original fragment.
So instead of:
setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
it should be
fragment.setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
https://github.com/tevjef/Rutgers-Course-Tracker/issues/8
Following are some helpful resources:
https://github.com/lgvalle/Material-Animations
http://www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html
https://www.youtube.com/watch?v=5e1Yh0fSZhQ
The key is to use a custom transaction with
transaction.addSharedElement(sharedElement, "sharedImage");
Shared Element Transition Between Two Fragments
In this example, one of two different ImageViews should be translated from the ChooserFragment to the DetailFragment.
In the ChooserFragment layout we need the unique transitionName attributes:
<ImageView
android:id="#+id/image_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_first"
android:transitionName="fistImage" />
<ImageView
android:id="#+id/image_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_second"
android:transitionName="secondImage" />
In the ChooserFragments class, we need to pass the View which was clicked and an ID to the parent Activity wich is handling the replacement of the fragments (we need the ID to know which image resource to show in the DetailFragment). How to pass information to a parent activity in detail is surely covered in another documentation.
view.findViewById(R.id.image_first).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mCallback != null) {
mCallback.showDetailFragment(view, 1);
}
}
});
view.findViewById(R.id.image_second).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mCallback != null) {
mCallback.showDetailFragment(view, 2);
}
}
});
In the DetailFragment, the ImageView of the shared element also needs the unique transitionName attribute.
<ImageView
android:id="#+id/image_shared"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:transitionName="sharedImage" />
In the onCreateView() method of the DetailFragment, we have to decide which image resource should be shown (if we don't do that, the shared element will disappear after the transition).
public static DetailFragment newInstance(Bundle args) {
DetailFragment fragment = new DetailFragment();
fragment.setArguments(args);
return fragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_detail, container, false);
ImageView sharedImage = (ImageView) view.findViewById(R.id.image_shared);
// Check which resource should be shown.
int type = getArguments().getInt("type");
// Show image based on the type.
switch (type) {
case 1:
sharedImage.setBackgroundResource(R.drawable.ic_first);
break;
case 2:
sharedImage.setBackgroundResource(R.drawable.ic_second);
break;
}
return view;
}
The parent Activity is receiving the callbacks and handles the replacement of the fragments.
#Override
public void showDetailFragment(View sharedElement, int type) {
// Get the chooser fragment, which is shown in the moment.
Fragment chooserFragment = getFragmentManager().findFragmentById(R.id.fragment_container);
// Set up the DetailFragment and put the type as argument.
Bundle args = new Bundle();
args.putInt("type", type);
Fragment fragment = DetailFragment.newInstance(args);
// Set up the transaction.
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Define the shared element transition.
fragment.setSharedElementEnterTransition(new DetailsTransition());
fragment.setSharedElementReturnTransition(new DetailsTransition());
// The rest of the views are just fading in/out.
fragment.setEnterTransition(new Fade());
chooserFragment.setExitTransition(new Fade());
// Now use the image's view and the target transitionName to define the shared element.
transaction.addSharedElement(sharedElement, "sharedImage");
// Replace the fragment.
transaction.replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName());
// Enable back navigation with shared element transitions.
transaction.addToBackStack(fragment.getClass().getSimpleName());
// Finally press play.
transaction.commit();
}
Not to forget - the Transition itself. This example moves and scales the shared element.
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class DetailsTransition extends TransitionSet {
public DetailsTransition() {
setOrdering(ORDERING_TOGETHER);
addTransition(new ChangeBounds()).
addTransition(new ChangeTransform()).
addTransition(new ChangeImageTransform());
}
}
I searched for SharedElement in fragments and I find very useful source code on GitHub.
1.first you should define transitionName for your Objects(Like ImageView) in both Fragments layout(We add a button in fragment A for handling click event):
fragment A:
<ImageView
android:id="#+id/fragment_a_imageView"
android:layout_width="128dp"
android:layout_height="96dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="80dp"
android:scaleType="centerCrop"
android:src="#drawable/gorilla"
android:transitionName="#string/simple_fragment_transition />
<Button
android:id="#+id/fragment_a_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="24dp"
android:text="#string/gorilla" />
fragment B:
<ImageView
android:id="#+id/fragment_b_image"
android:layout_width="match_parent"
android:layout_height="250dp"
android:scaleType="centerCrop"
android:src="#drawable/gorilla"
android:transitionName="#string/simple_fragment_transition" />
Then you should write this code in your transition file in transition Directory(if you haven't this Directory so create One: res > new > Android Resource Directory > Resource Type = transition > name = change_image_transform ):
change_image_transform.xml:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
</transitionSet>
In the last step you should complete codes in java:
fragment A:
public class FragmentA extends Fragment {
public static final String TAG = FragmentA.class.getSimpleName();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_a, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final ImageView imageView = (ImageView) view.findViewById(R.id.fragment_a_imageView);
Button button = (Button) view.findViewById(R.id.fragment_a_btn);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getFragmentManager()
.beginTransaction()
.addSharedElement(imageView, ViewCompat.getTransitionName(imageView))
.addToBackStack(TAG)
.replace(R.id.content, new FragmentB())
.commit();
}
});
}
}
fragment B:
public class FragmentB extends Fragment {
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_b, container, false);
}
}
don't forget to show your "A" fragment in your activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.content, new SimpleFragmentA())
.commit();
}
source : https://github.com/mikescamell/shared-element-transitions
How to start shared element transition using Fragments?
I assume you want to transition of your Image using Fragment (instead of Activity)
it wont work perfectly if you have already set AppTheme
keep the transition name of source and destination same
You have to do three things for transition:
1.Set transitionName to the source View(xml or programatically) -> before calling makeFragmentTransition
private void setImageZoom(boolean isImageZoom) {
ImageView imageView = this.findViewById(R.id.image);
if (isImageZoom) {
imageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ViewCompat.setTransitionName(imageView, "imageTransition");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
makeFragmentTransition(imageView);
}
}
});
}
}
2.Fragment Transition
Set TransitionSet for the specicific Transition animation
apply them on Fragment
call addSharedElement(View, transitionName) while fragmentTransition
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public void makeFragmentTransition(ImageView sourceTransitionView) {
//transtionName for sourceView
//MUST set transitionName before calling this method(programattically or give ->transitionName to the view in xml)
String sourceTransitionName = ViewCompat.getTransitionName(sourceTransitionView);
TransitionSet transitionSet = new TransitionSet();
transitionSet.setDuration(500);
transitionSet.addTransition(new ChangeBounds()); //to expand boundaries
transitionSet.addTransition(new ChangeTransform()); //for transtion vertically
transitionSet.addTransition(new ChangeImageTransform()); // image transform work
transitionSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
ImageTransitionFragment fragment = new ImageTransitionFragment();
fragment.setSharedElementEnterTransition(transitionSet);
fragment.setSharedElementReturnTransition(transitionSet);
fragment.setAllowReturnTransitionOverlap(false);
try {
getHostActivity().getSupportFragmentManager()
.beginTransaction()
//sharedElement is set here for fragment
//it will throw exception if transitionName is not same for source and destionationView
.addSharedElement(sourceTransitionView, sourceTransitionName)
//R.id.fragmentView is the View in activity on which fragment will load...
.replace(R.id.fragmentView, fragment)
.addToBackStack(null)
.commit();
} catch (Exception e) {
//
String string = e.toString();
}
}
3.set desitionNation transitionName in ImageView
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/destionationTransitionPage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transitionName="#string/pageTransition"
android:background="#color/black_color">
<com.android.foundation.ui.component.FNImageView
android:id="#+id/destinationImageView"
android:layout_width="#dimen/_400dp"
android:layout_gravity="center"
android:transitionName="imageTransition"
android:layout_height="#dimen/_400dp" />
</FrameLayout>
Please respond if anything is not clear or it need more improvement
Im updating some of my older projects and using fragments to update the look of things. I tried to use a fragment to launch a webview but when I try to run it I get the following error in my logcat.
E/Web Console(22464): Uncaught TypeError: Cannot read property 'addEventListener' of null at
http://m.yahoo.com/?.tsrc=yahoo&mobile_view_default=true:1
The way I used to use a webview was to just create a class that was its own activity that took place in a webview but I would like to have a small view within a fragment and then when I wanted to use the class I would launch it via intent and pass anything I needed to the webview like a url and other parameters in extras within the intent. I tried just setting up a webview within a fragment but I havent gotten it to work yet. This is the code Im using for the moment.
public class WebViewer extends Fragment {
WebView Wv;
String url = "http://www.yahoo.com";
Activity act;
public WebViewer(){}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.act = activity;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.webview, container);
Wv = (WebView) mView.findViewById(R.id.webview1);
Wv.getSettings().setJavaScriptEnabled(true);
Wv.getSettings().setRenderPriority(RenderPriority.HIGH);
Wv.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
Wv.getSettings().setDomStorageEnabled(true);
Wv.setWebViewClient(new HelloWebViewClient());
Wv.getSettings().setBuiltInZoomControls(true);
Wv.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress)
{
act.setTitle("Loading...");
act.setProgress(progress * 100);
if(progress == 100)
getActivity().setTitle(R.string.app_name);
}
});
Wv.loadUrl(url);
return mView;
}
}
And then this is the layout for the activity that uses this fragment.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#drawable/bggreydotted"
>
<fragment
android:id="#+id/webFragment"
android:layout_width="150dip"
android:layout_height="match_parent"
android:name="my.proj.WebViewer"></fragment>
</LinearLayout>
So how can I get a webview to open inside a fragment I can use in a view.
Have you thought about just extending WebViewFragment?
I currently have my application set up with a ListFragment on the left and a DetailsFragment on the right (similar to the layout on the tablet below).
On the details fragment (fragment next to the list) I have a goto deal button, which when pressed should replace the detailsFragment with a WebViewFragment.
The problem I am having is that when trying to load a url in the webviewfragment the WebView is null.
WebViewFragment webViewFragment = new WebViewFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.deal_details_fragment, webViewFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
// Set the url
if (webViewFragment.getWebView()==null)
Log.d("webviewfragment", "is null");
webViewFragment.getWebView().loadUrl("http://www.google.com");
Below is my main layout which has the original two fragments defined.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main_activity_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<fragment
android:name="com.bencallis.dealpad.DealListFragment"
android:id="#+id/deal_list_fragment"
android:layout_weight="1"
android:layout_width="0px"
android:layout_height="match_parent" >
<!-- Preview: layout=#layout/deal_list_fragment -->
</fragment>
<fragment
android:name="com.bencallis.dealpad.DealDetailsFragment"
android:id="#+id/deal_details_fragment"
android:layout_weight="2"
android:layout_width="0px"
android:layout_height="match_parent" >
<!-- Preview: layout=#layout/deal_details_fragment -->
</fragment>
</LinearLayout>
It seems that the webViewFragment is not being created fully as the WebView has not been initialised. I have looked online but there is very little information regarding the WebViewFragment.
Any ideas how to ensure WebView is initialised in the WebViewFragment?
With great help from Espiandev I have managed to get a working WebView. To ensure that links opened in the fragment and not in a web browser application I created a simple InnerWebView client which extends WebViewClinet.
public class DealWebViewFragment extends Fragment {
private WebView mWebView;
private boolean mIsWebViewAvailable;
private String mUrl = null;
/**
* Creates a new fragment which loads the supplied url as soon as it can
* #param url the url to load once initialised
*/
public DealWebViewFragment(String url) {
super();
mUrl = url;
}
/**
* Called to instantiate the view. Creates and returns the WebView.
*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (mWebView != null) {
mWebView.destroy();
}
mWebView = new WebView(getActivity());
mWebView.setOnKeyListener(new OnKeyListener(){
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return false;
}
});
mWebView.setWebViewClient(new InnerWebViewClient()); // forces it to open in app
mWebView.loadUrl(mUrl);
mIsWebViewAvailable = true;
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);
return mWebView;
}
/**
* Convenience method for loading a url. Will fail if {#link View} is not initialised (but won't throw an {#link Exception})
* #param url
*/
public void loadUrl(String url) {
if (mIsWebViewAvailable) getWebView().loadUrl(mUrl = url);
else Log.w("ImprovedWebViewFragment", "WebView cannot be found. Check the view and fragment have been loaded.");
}
/**
* Called when the fragment is visible to the user and actively running. Resumes the WebView.
*/
#Override
public void onPause() {
super.onPause();
mWebView.onPause();
}
/**
* Called when the fragment is no longer resumed. Pauses the WebView.
*/
#Override
public void onResume() {
mWebView.onResume();
super.onResume();
}
/**
* Called when the WebView has been detached from the fragment.
* The WebView is no longer available after this time.
*/
#Override
public void onDestroyView() {
mIsWebViewAvailable = false;
super.onDestroyView();
}
/**
* Called when the fragment is no longer in use. Destroys the internal state of the WebView.
*/
#Override
public void onDestroy() {
if (mWebView != null) {
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
/**
* Gets the WebView.
*/
public WebView getWebView() {
return mIsWebViewAvailable ? mWebView : null;
}
/* To ensure links open within the application */
private class InnerWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
Hopefully this is useful to someone in the future.
EDIT: So I played around with this for a while and it seems that the WVF is a bit rubbish and designed to be overridden. However, there's no documentation on this at all! The problem stems from the fact you can call getWebView() before the Fragments view is loaded, hence your NullPointerException. Except there isn't any way to detect when the Fragment's view has been loaded, so you're kind of stuck!
Instead I overrode the class, adding bits and changing bits, so that now it will work fine.
Check this link for the code. Then instead of using:
WebViewFragment webViewFragment = new WebViewFragment();
to load your Fragment, use:
ImprovedWebViewFragment wvf = new ImprovedWebViewFragment("www.google.com");
This class also includes a convenience method for loading a url, that won't throw an Exception if there's no WebView.
So, no, I don't think there's a particularly simple way for using the built-in WebViewFragment, but it is pretty easy to make something that works instead. Hope it helps!
WebViewFragment as is is not that straightforward to use. Try this simple extension (You can copy/paste):
public class UrlWebViewFragment extends WebViewFragment{
private String url;
public static UrlWebViewFragment newInstance(String url) {
UrlWebViewFragment fragment = new UrlWebViewFragment();
fragment.url = url;
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
WebView webView = (WebView) super.onCreateView(inflater, container, savedInstanceState);
webView.loadUrl(url);
return webView;
}
}
Call where you need using the factory method:
WebViewFragment fragment = UrlWebViewFragment.newInstance("http://ur-url.com");
Fragments can only be replaced if they were initiallized in Java, not XML. I think so, I had the same problem and it solved it. Change your XML to this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main_activity_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<fragment
android:name="com.bencallis.dealpad.DealListFragment"
android:id="#+id/deal_list_fragment"
android:layout_weight="1"
android:layout_width="0px"
android:layout_height="match_parent" >
<!-- Preview: layout=#layout/deal_list_fragment -->
</fragment>
<View
android:id="#+id/my_container"
android:layout_weight="2"
android:layout_width="0px"
android:layout_height="match_parent" >
</View>
</LinearLayout>
and then in Java, your onCreate method:
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.my_container, new DealDetailsFragment());
transaction.commit();
or even better create whole method to just deal with Transactions.
Now Transaction from your question should work. :)