Animate search bar to toolbar android - android

I'm trying to build a search bar similar to Flipboard. This search bar animates from below the toolbar to cover the toolbar. This GIF shows it better than I can explain:
Does anyone know if this is standard Material Design? And if so, are there any libraries or standard widgets I can use to do this? Soundcloud also does this so just wanted to ask if there was anything already out there. If not I'll just have to implement it myself.
Thanks!

To solve this I'd recommend looking into android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation().
Here are the snippets from the code I remember:
I have ActivityA which contains an EditTextA.
The EditTextA has a View.OnClickListener:
View.OnClickListener() {
#Override
public void onClick(View view) {
ActivityB.launch(getActivity(), view);
}
}
which when invoked calls a static method: ActivityB.launch(Activity a, View transitionView):
public static void launch(Activity activity, View transitionView) {
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
activity, transitionView, EXTRA_TRANSITION_VIEW);
Intent intent = new Intent(activity, ActivityB.class);
ActivityCompat.startActivity(activity, intent, options.toBundle());
}
The code above is basically creating an Intent that will launch ActivityB. That Intent also includes a Bundle (options.toBundle()) which has a bunch of stuff, including the view that is transitioning from ActivityA to ActivityB. In my case this is the EditTextA.
In ActivityB.onCreate() all we need to do is "connect" that view to the new view it's transitioning to. So in my case, EditTextA is transitioning to another EditTextB that lives in ActivityB.
ActivityB.onCreate() {
....
mEditTextB = (EditText) findByViewId(...);
ViewCompat.setTransitionName(mEditTextB, EXTRA_TRANSITION_VIEW);
}
If I remember everything correctly that should be it :).

Related

Fragment not attached to host

Let me explain the whole thing, just in case. I'm using BottomNavigationView with Jetpack's Mobile Navigation graphs and so. I reimplemented the method:
NavigationUI.setupWithNavController(...)
during navigation setup, instead of using this method I made a very similar:
customSetupWithNavController(...)
the only changes I made was to change de default fading transition between fragments, and started to using more natural (in my opinion) side slide animations, just like this in onNavDestinationSelected:
if(origin == 0){
builder.setEnterAnim(R.anim.slide_in_from_right)
.setExitAnim(R.anim.slide_out_to_left)
.setPopEnterAnim(R.anim.slide_in_from_left)
.setPopExitAnim(R.anim.slide_out_to_right);
}else if(origin == 1){
builder.setEnterAnim(R.anim.slide_in_from_left)
.setExitAnim(R.anim.slide_out_to_right)
.setPopEnterAnim(R.anim.slide_in_from_right)
.setPopExitAnim(R.anim.slide_out_to_left);
}
Where origin stands for the direction where the incoming Fragment comes from.
It came with a problem: 2 of my fragments need a FAB to add elements to a recycler, and the side slide transitioning suddenly became ugly. So I added a single FAB in MainActivity's Layout, and a logic shows the FAB only when these 2 fragments are called.
I couldn't find a nice way to pass the click event from Activity to Fragments, because I wasn't able to instantiate the Fragments, since the Navigation handles the whole process.
So, what I did was to create a ViewHolder, since I know it can survive trough lifecycle changes. This ViewHolder holds an int, and a MutableLiveData, in the MainActivity logic I pass the current selected id of the element selected by the BottomNavigationView to the int, and only if the MainActivity's FAB is clicked the live Boolean is set to true. So, in Fragments onViewCreated() I added and observer to this Boolean, when the value is set to true, and the id passed to the ViewHolder matches with the id of the current fragment, the Boolean is set back to false, and something can be done, it's something like this:
eventsNotificationHandler.getClickEvent().observe(requireActivity(), new Observer<Boolean>() {
#Override
public void onChanged(Boolean aBoolean) {
if(aBoolean && eventsNotificationHandler.getPositionId() == R.id.nav_contacts){
eventsNotificationHandler.setClickEvent(false);
//do something here
}
}
});
This notificationHandler is the ViewHolder.
So far so good, at this point I can:
1- Navigate between BottomNavigationView' Fragments freely, and the FAB shows only for needed fragments.
2- Use Log.d(...) inside the observer any time I want, and see that the debug message just fine.
3- Toast a message, any time I want, ONLY if the context parameter was initialized outside the Observer, something like this:
Toast.makeText(previouslyDefinedContext, "SOME TEXT", Toast.LENGTH_SHORT).show();
What I can't:
1- Launch an Activity whenever I want, from inside the observer by using same idea than before, ONLY initializing the context before, and outside the Observer I was able to start the intent, just like this:
eventsNotificationHandler.getClickEvent().observe(requireActivity(), new Observer<Boolean>() {
#Override
public void onChanged(Boolean aBoolean) {
if(aBoolean && eventsNotificationHandler.getPositionId() == R.id.nav_contacts){
eventsNotificationHandler.setClickEvent(false);
Intent newContact = new Intent(previouslyDefinedContext, NewContactActivity.class);
startActivity(newContact);
requireActivity().overridePendingTransition(R.anim.slide_in_from_right,R.anim.slide_out_to_left);
}
}
});
But in this particular case I can launch the new Activity as many times as I want, BUT ONLY if I navigate directly to this particular fragment where the observer is defined after the app opens, if I decide to navigate first trough some other fragments instead, and then I go to this fragment to try to launch the Activity, the app crashes
I've noticed that this exact behavior happens when I call requireContext() from inside the Observer, it works but then stops working.
The app crashes with:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: cu.arrowtech.bpawreckproject, PID: 18019
java.lang.IllegalStateException: Fragment FragmentContacts{883259b} (9f127bdb-127d-4366-b90b-c8900a5a771e)} not attached to Activity
What I want:
1- A right way to launch a new Activity from inside a Fragment, by pressing a FAB in MainActivity, if it's possible.
2- A nice way to switch fragments if a possible solution implies to change the logic I have already.
3- Keep using Jetpack and Navigation Graphs.
I'm able to do what I want by using 2 separate FABs in each Fragment, but the transitioning is not nice and beautiful.
I'm open to suggestions, even if that implies to change the logic. I'm almost certain it must be a better way to do what I'm doing, instead of using ViewHolder for this purpose.
I would like to get something similar to the Google Pay, it seems to be that the Buttons for adding payment method, passes, and transfers is the same button, but it adapts to each situation.
After some reasearch I did found a way to keep fluid transitions between fragments AND Mobile Navigation components AND a single FAB in the MainActivity layout.
What I did was to use an Interface instead of ViewModels (I always knew that approach was wrong):
public interface SharedViewsInterface {
void onFabClicked();
}
So in my MainActivity I defined that interface as null, and created a method to define it according to the situation. My MainActivity looks like:
public class MainActivity extends AppCompatActivity {
//UI elements
private FloatingActionButton main_fab;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Same dode to get the custom animations
//.......
//Main and only Fab configuration
main_fab = findViewById(R.id.main_fab);
main_fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(sharedViewsInterface !=null){
sharedViewsInterface.onFabClicked();
}
}
});
}
//Other auxiliary methods
public void setInterface(SharedViewsInterface sharedViewsInterface){
this.sharedViewsInterface = sharedViewsInterface;
}
}
By doing this, I can, from each fragment to implement the Interface in onCreate by doing this:
((MainActivity) requireActivity()).setInterface(new SharedViewsInterface() {
#Override
public void onFabClicked() {
Intent newContact = new Intent(requireContext(), NewContactActivity.class);
startActivity(newContact);
requireActivity().overridePendingTransition(R.anim.slide_in_from_right_short,R.anim.slide_out_to_left_medium);
}
});
This works well becouse the FAB is shown only when a fragment with Interface implementation is visible, see my example gif

Activity shared element transition using MVVM architecture

I'm learning MVVM pattern and have an issue right now. I have an activity A with imageview on it, and an activity B with same imageview but in another place and bigger. When in activity A i click on imageview, i want to start activity B and imageview should be shared element to achieve what i want with nice animation.
Using databinding i handle click on imageview in my view model:
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:onClick="#{(v) -> user.onAvatarClick(v)}"
android:scaleType="centerCrop"
android:src="#{user.photoUrl}"
android:transitionName="#string/avatar_transition" />
And in my viewmodel i should wrote something like this:
public void onAvatarClick(View view) {
Intent intent = new Intent(context, AvatarActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, view, view.getTransitionName());
context.startActivity(intent, options.toBundle());
}
But, the problem is that my viewmodel knows nothing about activity. And i have no idea how to do what i want.
One possible solution is create interface with method like startActivityB(View view) and implement it in activity and set it to field for example viewModelListener. And then i can wrote something like:
public void onAvatarClick(View view) {
viewModelListener.startActivityB(view);
}
But in this case my viewmodel will have reference to view, and it broke MVVM main idea, right?
So, what the right way to start new activity with shared element using MVVM architecture?
This can be done through Live Data in Android. As you can observe the changes in live data and navigate to other activity accordingly.
Example here
In your xml file where you clicked the button
android:onClick="#{()->homeActivityViewModel.openNewActivity()}"
In your viewModel class
private final MutableLiveData<Boolean> openNewScreen = new MutableLiveData<>();
//function that is binded to xml
public void openNewActivity() {
openNewScreen.setValue(true);
}
public MutableLiveData<Boolean> getNewScreen() {
return openNewScreen;
}
In your activity
homeActivityViewModel.getNewScreen().observe(this,
start -> {
if (start) {
Intent intent = new Intent(this, NewActivity.class);
startActivity(intent);
} });

How to implement the “parent-to-child” navigational transition as prescribed by Material Design [duplicate]

Google's Material Design guidelines prescribe the following transition for "parent-to-child" transitions when the parent consists of a list. (Material Design Guidelines)
How do I provide such a transition? I'm unaware of any inbuilt transition provided to make this possible.
One option is to use ActivityOptionsCompat.makeScaleUpAnimation
Activity activity = getActivity();
Intent intent = new Intent(activity, OtherActivity.class);
Bundle options = ActivityOptionsCompat.makeScaleUpAnimation(
sourceView, 0, 0, sourceView.getWidth(), sourceView.getHeight()).toBundle();
ActivityCompat.startActivity(activity, intent, options);
This will cause the new activity to expand vertically and horizontally outwards from your sourceView
Start an activity with a shared element
To make a screen transition animation between two activities that have a shared element:
Enable window content transitions in your theme.
Specify a shared elements transition in your style.
Define your transition as an XML resource.
Assign a common name to the shared elements in both layouts with the android:transitionName attribute.
Use the ActivityOptions.makeSceneTransitionAnimation() method.
// get the element that receives the click event
final View imgContainerView = findViewById(R.id.img_container);
// get the common element for the transition in this activity
final View androidRobotView = findViewById(R.id.image_small);
// define a click listener
imgContainerView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(this, Activity2.class);
// create the transition animation - the images in the layouts
// of both activities are defined with android:transitionName="robot"
ActivityOptions options = ActivityOptions
.makeSceneTransitionAnimation(this, androidRobotView, "robot");
// start the new activity
startActivity(intent, options.toBundle());
}
});
For shared dynamic views that you generate in your code, use the View.setTransitionName() method to specify a common element name in both activities.
To reverse the scene transition animation when you finish the second activity, call the Activity.finishAfterTransition() method instead of Activity.finish().
Take from here Customize Activity Transitions
Hey I think I'm a few weeks late, but I just released a library for building this, inspired by Google Inbox: https://github.com/saket/InboxRecyclerView
you can animation using below code
Intent intent = new Intent(MainActivity.this, NFCTagInformationActivity.class);
Bundle options = ActivityOptionsCompat.makeClipRevealAnimation(
cvTagInfoSmall, 0, 0, cvTagInfoSmall.getWidth(), cvTagInfoSmall.getHeight()).toBundle();
ActivityCompat.startActivity(this, intent, options);
you can use makeScaleUpAnimation instead of makeClipRevealAnimation for different view transition animation.

recyclerview open activity with navbar on click item

I'm new android developper. In my application I have a main activity with toolbar, that contains a title, and a recycler view.
The recycler view contains some items. I want to open a activity on click on them. My code is able to open the activity but the toolbar disappear.
I open the activity like this:
public VHolder(final View itemView){
super(itemView);
title = ((TextView) itemView.findViewById(R.id.articleTitle));
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(itemView.getContext(), ArticleActivity.class);
intent.putExtra("title", currentNews.title);
intent.putExtra("content", currentNews.htmlContent);
itemView.getContext().startActivity(intent);
}
});
}
Have you any ideas and advices ?
Sorry about my poor english ;).
You are doing everything in the correct way. The problem is that toolbar is just another widget on your Activity and cannot be shared between multiple activities. So you should add toolbar view to the layout of the Activity which you are starting (ArticleActivity).
As another option you can show Fragment over RecyclerView instead of starting new Activity. Similar to this: how to open a different fragment on recyclerview OnClick
I have found the cause of my problem of toolbar. The opened activity (ArticleActivity) not extends "AppCompatActivity" like the main activity but, "Activity".

Start another Activity inside the FrameLayout of TabActivity

I am trying to make an application which have 4 tabs at the bottom of the screen.
All of them contain Activity (Intent).
And I want to navigate any of the Activity to another activity. But want to keep the TabWidget visible.
Let me know as quickly as possible if you know about it.
Shaiful
The problem of error occuring due to the replacement of activities can be solved in the following manner.
First Let us understand the flow:
We have in a Tab host , activity (say a list) from which we need to go to the next Activity (say details for the clicked item) under the same tab. For this we can use the concept of replacing the activity.Also setting the flags for the tab selected and other for knowing that details are being shown now
When we press back we should get the previous activity under the same tab.For this instead of again replacing the activity we can refresh the tab while using the particular flag for tab which was selected. Also if flag for show details is true we'll go the the list in the same tab or else we will go the activity before the tabwidget (normal use of onBackPressed)
The code can be as follows..
For going from list to details...
(This can be in the onClickListener)
private OnClickListener textListener = new OnClickListener() {
#Override
public void onClick(View v) {
Constants.SHOW_DETAILS = true;
Intent intent = new Intent(context, DetailsActivity.class);
replaceContentView("activity3", intent);
}
};
public void replaceContentView(String id, Intent newIntent) {
View view = ((ActivityGroup) context)
.getLocalActivityManager()
.startActivity(id,
newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))
.getDecorView();
((Activity) context).setContentView(view);
}
When back pressed is done we override on BackPressed in each of the Activity under the tab to go to the list again from the details screen
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
if (MathHelper.SHOW_DETAILS) {
Log.e("back", "pressed accepted");
Constants.LIST_ACTIVITY = 1;
Constants.SHOW_DETAILS = false;
Intent intent = new Intent(this, Tab_widget.class);
startActivity(intent);
finish();
}
}
The most important part here is
Constants.LIST_ACTIVITY = 1; it indicates which tab we are in. so the corresponding activities will have its value as 0,1,2...etc
Again to load the correct list (Activty) when the tab activity is refreshed we have to include this in the TabWidget onCreate after the creation of the tabs
tabHost.setCurrentTab(Constants.LIST_ACTIVITY);
This is implemented in Tabs with multiple activities in a single tab.
However when multiple times activities are called StackOverFlow error arises. Tried very hard but unable to solve it.. Please someone tell a method to solve this problem
Also need to Replace an activity in a tab, However from child activity. How is that to be done?
At any one moment there may only be one activity. Docs about this here

Categories

Resources