change transition shared element on activity exit - android

I have a recycler view containing some images and when i click on an image it opens a new activity with a view pager to show those images.
this openning contains a shared element transition. now when press the back button i want to change the transiting view to the currently viewing image.
how can i do this
I already tried setExitSharedElementCallback on the first activity which updates List<String> names and Map<String, View> sharedElements on onMapSharedElements to appropriate ones. but still no transition happens.
is there anything else I should do?

after more works i find out that i should also add
setEnterSharedElementCallback(new SharedElementCallback() {
#Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
// update names and sharedElements
}
}
to the second activity to update its transition views.

Related

android - RecyclerView items: how to save properties of Views in every row?

My problem is to understand how RecyclerView works.. I have RecyclerView with a little bit complicated item in every row, but the main thing is that Item has a child ImageView and LinearLayout. I want to press ImageView and set Visibility of LinearLayout to GONE or VISIBLE and rotate my ImageView. I tried to do this in my onBindViewHolder:
holder.mIVExpandBtn.setOnClickListener(new OnClickListener() {
boolean isOpen = false;
#Override
public void onClick(View v) {
if (isOpen) {
CounterListAdapter.this.notifyItemChanged(position);
holder.mLLDetails.setVisibility(GONE);
holder.mDivider.setVisibility(VISIBLE);
holder.setArrowUp(false);
isOpen = false;
counterItem.setDetailsOpened(false);
} else {
holder.mLLDetails.setVisibility(VISIBLE);
holder.mDivider.setVisibility(GONE);
holder.setArrowUp(true);
isOpen = true;
counterItem.setDetailsOpened(true);
}
}
});
And I have some problems here.
I have a boolean variable inside OnClickListener, I know its wrong, so it changes only one time when I expand my LinearLayout. If I make this boolean global variable, if I expand one row of RecyclerView isOpen = true for any other item and it doesn't expand itself when I click on ImageView.. Where should I place this boolean?
And the second question - how can I save the state of my RecyclerView rows on screen rotation? For example I expanded one of my rows, LinearLayout.setVisibility(VISIBLE), change screen orientation and its closed.
For your first problem, you should put your boolean variable where you also define your views, i.e., inside your ViewHolder, ir order that onClick you call the boolean this way
if(holder.isOpen)
In this way you keep the reference of each boolean to each row.
For your second problem, the solution is pretty simple. In your manifest, in the activity where you have your RecyclerView, define the following:
android:configChanges="keyboardHidden|orientation|screenSize"
This prevents your activity from being recreated on configuration change in case you rotate the screen, so the activity will keep it's state and your RecyclerView will therefor not be recreated along with your adapter.
Notice that this means that, if your activity is not recreated, onPause, onStop, etc, will not run. This is only for screen rotation, your activity will still run the method onConfigurationChanged() which is where you should define any changes you need in case the screen rotates.
You better put the OnClickListener in the Holder class, something like this:
private class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
#Override
public void onClick(View view) {
....
}
About how to save state I think all things that define the rows should be saved in the array you pass to the adapter contructor, you can add fields in the array item object that save VISIBILITY state of the row views.
On screen rotation then two options:
1 - as #Ricardo said avoiding Activity recreation
2 - onSaveInstanceState / onRestoreInstanceStates save/restore the array that define the rows .. my prefered method for that is to use JSON and put it in a String that can be saved/restored in the Bundle.

How to maintain Android GridView position in all cases?

I'm new to Android and I'm trying to do the following task on my school project:
I have a grid view of movies which the user can scroll endlessly.
When the app starts I fetch the first 20 movies and each time the user scrolls to the bottom of the grid I execute an AsyncTask to fetch 20 more movies and add them to the Adapter.
When the user clicks on a movie he goes to a new child activity to see the movie details.
I'm having troubles maintaining the GridView's scroll position in the following cases:
When the user goes to the details activity and returns to the main activity of the movies.
When the user changes the device orientation.
And when dealing with theses 2 cases I also need to take in consideration that maybe the user scrolled a lot and had 100 movies in the adapter and when he goes back the activity start from the start with only the first 20 movies, so I would be able to scroll to his last position.
Can someone please tell me how can I give the best user experience in my project by not losing the user's scroll position at any case?
I don't know if this is the best practice, but in my case it is.
I decided to set my adapter as a global static variable, in this way I maintain the amount of data loaded via the API, and I don't need to perform a request for every time the user moves between activities.
For maintaining the scroll position I used the onItemClickListener when moving to the details activity and the savedInstanceState when changing orientation.
Here is my code for that:
//Static variables
private static MoviesAdapter mMoviesAdapter;
private static int mGridViewPosition = 0;
//Call this method when user clicks the back button
public static void ClearStaticData(){
mMoviesAdapter.clear();
mMoviesAdapter = null;
}
#Override
public void onSaveInstanceState(Bundle outState) {
int index = mGridView.getFirstVisiblePosition();
outState.putInt(GRID_VIEW_POSITION, index);
super.onSaveInstanceState(outState);
}
#Override
public View onCreateView(...) {
if (mMoviesAdapter == null) {
mMoviesAdapter = new MoviesAdapter(...);
} else {
RestoreGridPosition();
}
}
private void RestoreGridPosition(){
if(mGridViewPosition > 0 && mMoviesAdapter.getCount() >= mGridViewPosition)
mGridView.setSelection(mGridViewPosition);
}
Since I fill my adapter via API call, I think this is probably the best solution to save the data and not to perform requests every time.
Try not finishing mainActivity once a gridItem is clicked so when user navigates back to mainActivity (from detailsActivity) he will have all the data that was there before.
You can handle this situation with activity's lifecycle callbacks:
You can get currently visible GridView item's position like this:
int mCurrentPosition = gridview.getFirstVisiblePosition();
When an orientation change is occurring the activity is recreated and going through the following stages:
onSaveInstanceState
onRestoreInstanceState
You can then save the position before orientation change is happening and get it back when its being restored.
Save Your Activity State
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current scroll state
savedInstanceState.putInt(STATE_POSITION, mCurrentPosition);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
Restore Your Activity State
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentPosition = savedInstanceState.getInt(STATE_POSITION);
}
Here once you have the previous position you can move to the desired position in the gridView:
gridview.smoothScrollToPosition(int mCurrentPosition)
This is taken from android docs: Recreating an Activity
Scrolling gridView to position GridView scrolling stackoverflow

Shared elements animation with AppCompat inside a RecyclerView

I have a Fragment with some ImageViews, if I click on one of this ImageViews the app open a new activity where the just clicked "target" image is at the top of it.
In the first place I put the ImageView in the new Activity directly in the parent layout.
Now in this activity I've set a custom Header as the first item of the RecyclerView, the "target" ImageView is in this new header.
Now the transition doesn't work anymore. I've thought that the content of the RecyclerView is drawn later, so I've tryed with
ActivityCompat.postponeEnterTransition(this);
before setContentView (I dont know if it's correct)
and
ActivityCompat.startPostponedEnterTransition(mContext);
in onBindViewHolder of the RecyclerView.
But it doesn't work...what can I do?
I solved by using
setEnterSharedElementCallback(new SharedElementCallback() {
#Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
sharedElements.put("profile_user_img", image);
super.onMapSharedElements(names, sharedElements);
}
});
in conjunction with postponeEnterTransition and startPostponedEnterTransition

Programmatic fragment within fragment within viewpager duplication

I have a very simple ToDo list app with multiple categories the user can add an item to. The architecture looks something like this:
http://i.imgur.com/WA3dtcU.png
I am using a ViewPager and each view is a fragment that I instantiate in the ViewPagerAdapater:
public Fragment getItem(int i) {
ToDoCategoryModel cat = categories().get(i);
ToDoCategoryView view = ToDoCategoryView.newInstance(cat);
return view;
}
Within each of these category the user can add new Item Fragments to a Linear Layout.
A very strange thing happens though. I start off on Category 1 (C1) Which currently has 1 Item in the list. Then scroll to C2 then scroll again to C3. Then I scroll back to C2. The ViewPager asks my ViewPagerAdapter to re-create C1 as it was destroyed after having scrolled 2 pages away from it. This is fine, I just create a brand new fragment (as the code above shows). However, when I now scroll back to C1, instead of 1 Item being shown (as you would expect) there are 2 items shown. They are a duplicate of each other.
The code I use to add the Item fragment to the Category fragment is:
public void addToDoItem(ToDoItemModel model) {
ToDoItemView newItem = ToDoItemView.newInstance(model, this);
getChildFragmentManager().beginTransaction().add(_linearLayout_items.getId(), newItem, "sameTag"+count++).commit();
}
If I change this code to add new TextView instead of fragments:
public void addToDoItem(ToDoItemModel model) {
ToDoItemView newItem = ToDoItemView.newInstance(model, this);
TextView tv = new TextView(getActivity());
tv.setText(newItem.getModel().getName());
_linearLayout_items.addView(tv);
}
It works fine! There are no duplicate items shown.
So what is wrong with adding fragments dynamically using the child fragment manager within a view pager?
Thanks.

Listview child should link to another child

I created an expandable listview based on this link. Its just working fine. Now what i want is
1) How to make a childview to link another sub-child view
2) The sub- child view should be open as a new list view on the window(Right side of the view) is my expected layout. I googled but couldn't find how to achieve this. Please help me in achieving this. Thanks in advance.
You'll need to specify and handle onClick event of ListView row items.
Then you'll open a new Activity, based on the item clicked.
Parameters for new activity are supplied through intent extras, the new activity can use these values to get data from cloud or process the values to show certain results.
I've used CustomAdapter class several times to handle this scenario.
class Ocl implements OnClickListener{
#Override
public void onClick(View v) {
Intent intDetail = new Intent(getActivity(), PartDetail.class);
intDetail.putExtra(_ID, mParts[position].getSPr());
intDetail.putExtra(_LOT, mParts[position].getLotID());
intDetail.putExtra(_QTY, mParts[position].getQty());
intDetail.putExtra(_UID, mParts[position].getPartID());
startActivity(intDetail);
}
}
So, do you want your first child to expand into another ListView? Or maybe just open another Activity/Fragment that contains the matching ListView?
In case you want to the the first, you could design a CustomLayout for the Childview, which on OnClick expands, and changes its content to a specific ListView.
Otherwise you would just open up another ListView with data depending on Which Child in First List was Clicked.
Well, i am using some Like that to enlarge ChildViews on Click to show me detailed information.
Im using a Class to wrap my Data named Row. These Rows indicate if they are clickable and if so, the ListView will allow clicks on the rows. A Click will then be handled by the Class itself, making the displayed Text longer(more Detailed). And in order to relayout the items, you need to call notifyDataSetChanged.
#Override
public void onClick(Context context, MyExpandableListAdapter mela) {
this.setBig(!isBig());
mela.notifyDataSetChanged();
}
So, i would handle the row state (expanded/normal) in the getView Method of parents Adapter, to decide which childLayout i inflate.
would looke something like this
public View getView (args...) {
Object data = getItem(position);
if (data.isExpanded()) {
//inflate ListView Layout, create Addapter fill it....
} else {
//show some title or whatever to identify row.
}
}

Categories

Resources