Ripple effect not working with shared element transition and RecyclerView - android

I have a shared element transition when a `RecyclerView item click starts a detail Activity, but the ripple effect on the item click is never visible
Start Activity with shared element transition
Intent intent = IntentUtils.createDetailsIntent(InspectionListFragment.this.getContext(), record);
Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
view, getString(R.string.transition_element)).toBundle();
getActivity().startActivity(intent, options);
I noticed this log message
D/OpenGLRenderer: endAllStagingAnimators on 0x95e86600 (RippleDrawable) with handle 0xaa6c2760
If I remove the transition, the ripple works (and I don't see this message).
Delay Activity start using Handler
If I use a Handler with postDelayed to start the Activity, the results were mixed. I see the ripple, but the transition is not as smooth:
handler.postDelayed(new Runnable() {
#Override
public void run() {
Intent intent = IntentUtils.createDetailsIntent(InspectionListFragment.this.getContext(), record);
Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
view, getString(R.string.transition_element)).toBundle();
getActivity().startActivity(intent, options);
}
}, 200);
Using ListView
Note that using a ListView with the same item layout and makeSceneTransitionAnimation works fine. Unfortunately this is not suitable.
The item layout
<LinearLayout
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"

I ran into the same issue when I had an ImageView within the card as the shared element. I was able to resolve it by using the CardView as the source view for the shared element transition (and the ripple effect) instead.
<android.support.v7.widget.CardView
android:id="#+id/itemCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="#dimen/card_corner_radius"
app:cardElevation="#dimen/card_elevation"
android:layout_gravity="center"
android:clickable="true"
android:onClick="#{onItemClick}"
android:foreground="?android:attr/selectableItemBackground"
>
I'm using data binding, but the shared element change was essentially just selecting a different source view:
// before:
//onItemClickListener.onItemClick(view, getAdapterPosition(), getItemId(), Pair.create((View)b.itemImage, "activity_image"));
// after:
onItemClickListener.onItemClick(view, getAdapterPosition(), getItemId(), Pair.create((View)b.itemCard, "activity_image"));
Before this, I also tried using postDelayed with a short delay, but I found that approach added too much delay to the navigation for my liking.

Related

Context Menu not appearing only at certain times on Edittext while in RecyclerView

I have a strange situation...
I have a RecyclerView with swipeRefresh enabled... and the 1st item in my RecyclerView is a CardView having
<com.daniribalbert.customfontlib.views.CustomFontEditText
android:id="#+id/start_chat_add_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:paddingTop="#dimen/general_gap_smallest"
android:paddingBottom="#dimen/general_gap_smallest"
android:gravity="top"
android:layout_toLeftOf="#id/start_chat_post_comment"
android:layout_toRightOf="#id/start_chat_attach_media"
android:background="#android:color/transparent"
android:imeOptions="actionDone"
android:textColorHint="#color/edit_text_hint_color"
android:textColor="#color/newsfeed_text_card_owner_main_text_color"
android:textSize="#dimen/font_normal"
app:font="Roboto-Regular"/>
When the recyclerview is inflated 1st time, I am able to longpress on the EditText and access the context menu. However each time I refresh the recyclerview by pulling swiperefresh (a new card is added as it was added for the 1st time), my context menu never appears on longpress on EditText.
JFYI ... i do not experience a certain vibration on the phone either on longpress on EditText at this point in time when the context menu does not show up.
I have tried looking out for some information and stuff, but nothing looks to be helpful, except at some point someone mentioned that context menu may not work if edittext is a part of popup (alertbox), and even the option of textIsSelectable = "true" did not come any handy.
Any help is much appreciated.
This fix worked for me:
edtImgDesc.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
#Override
public void onViewAttachedToWindow(View v) {
edtImgDesc.setCursorVisible(false);
edtImgDesc.setCursorVisible(true);
}
#Override
public void onViewDetachedFromWindow(View v) {
}
})
Taken from: Long pressed broken for EditText (Or android.support.v7.widget.AppCompatEditText) after the view is recycled in RecyclerView

What could be responsible for an ImageView not being clickable

I am using an ImageView as a NEXT button in my Android app which is responsible for loading the next page of results into the current activity. However, despite that I bind a click listener to it, I cannot seem to capture click events on this ImageView. Here is the XML:
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="50"
android:gravity="left"
android:orientation="horizontal">
<ImageView
android:id="#+id/listBackIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/back_icon"/>
<TextView
android:id="#+id/listBackLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Prev"
android:textSize="16dip"/>
</LinearLayout>
And here is the relevant Java code:
ImageView forwardIconView = (ImageView)findViewById(R.id.listBackIcon);
// not sure if necessary; doesn't fix it anyway
forwardIconView.setClickable(true);
forwardIconView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
++pageNumber;
try {
params.put("page", pageNumber);
} catch (JSONException e) {
// do something
}
ConnectionTask task = new ConnectionTask();
task.execute(new String[0]);
}
});
I spent about an hour researching this on Stack Overflow. I found a few places which claimed that ImageView could directly be made clickable, but most things recommended workarounds using other types of widgets.
Does anything about my layout/code stand out as being a culprit for this behavior?
Update:
I also tried binding a click listener to the TextView at the same level as the ImageView and this too failed to capture clicks. So now I am suspecting that the views are being masked by something. Perhaps something is capturing the clicks instead of the views.
I would set it up like this:
private ImageView nextButton;
nextButton = (ImageView)view.findViewById(R.id.back_button);
Util.loadImage(getActivity(),R.drawable.some_image,nextButton); //Here i would load the image, but i see you do it in XML.
nextButton.setOnClickListener(nextButtonListener);
nextButton.setEnabled(true);
View.OnClickListener nextButtonListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.v(TAG, "ImageView has been clicked. do something.");
}
};
This works for me.
Why not use android:drawableLeft attribute for the textview instead of using imageView​ and textview both in a linearlayout .
<ImageView
android:id="#+id/listBackIcon"
...
android:clickable="true"
Or you can try overriding onTouchListener with ACTION_DOWN event filter, not onClickListener. Also check for parrents with android:clickable="false", they could block childs for click events.
What seemed to work for me was the accepted answer from this SO question, which suggests adding the following the every child element of the LinearLayout which I pasted in my question:
android:duplicateParentState="true"
I don't know exactly what was happening, but it appears the click events were not making it down to the TextView and ImageView. Strangely, the click events were reaching a Button, when I added one for debugging purposes. If someone has more insight into what actually happened, leave a comment and this answer can be updated.

Lollipop activity shared element transition with fade

I'm trying to do a simple shared element transition where a row of the clicked list item transitions into the top header view of the resulting activity. I want the top header view to take the position of the list item, fade itself in, then transform to its final position.
This is the simple sharedElementEnterTransition that I wrote for the called activity:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<fade android:fadingMode="fade_out" />
<changeBounds />
<changeTransform />
<changeImageTransform />
</transitionSet>
However, the fade transition isn't doing anything. I see the resulting view suddenly pop up on the spot then transforms to the final position.
Here's where I start the activity
view.setOnClickListener(view -> {
final Intent intent = new Intent(...);
view.setTransitionName("test");
final ActivityOptions options =
ActivityOptions.makeSceneTransitionAnimation(activity, view, "test");
startActivity(intent, options.toBundle());
});
Can't quite figure out what's happening. Anyone has any ideas? The layout codes are exactly what you would expect with with android:transitionName="test".
Meanwhile debugging Visibility class, I figured out that target view was not the expected one, therefore I forced it to test it, and it seems working. However, I don't know if this is the perfect solution, because I also agreed that it should work as other transitions (automatically)
sharedElementEnterTransition = Fade(Visibility.MODE_IN).addTarget(your_view)
If you want a sequential transitions, you might need to create a TransitionSet and add it
val fadeIn = Fade(Visibility.MODE_IN).addTarget(your_view)
val move = TransitionInflater.from(context)
.inflateTransition(android.R.transition.move)
val transitionSet = TransitionSet().apply {
ordering = ORDERING_TOGETHER
addTransition(move)
addTransition(fadeIn)
}
sharedElementEnterTransition = transitionSet

Find and return the ID of the current view

I'm trying to set up a "close to start" button in a game. The game takes the user from view to view like a maze. The user could be on any view, but I want a way to get them back to the start screen. Is there a way to find and return the ID of the current view for my findViewByID? I've found a I've tried the following code in various forms:
OnClickListener closetomain = new OnClickListener() {
#Override
public void onClick(View v) {
int currentid = v.findFocus().getId();
RelativeLayout hideme = (RelativeLayout)findViewById(currentid);
hideme.setVisibility(View.GONE);
setContentView(R.layout.main);
// RelativeLayout showme = (RelativeLayout)findViewById(R.id.startLayout);
// showme.setVisibility(View.VISIBLE);
}
};
Okay, it turns out I should have given each close button the same ID and used theisenp's suggestion for the simplest code (well, that I could understand). It also turns out that I should have started by putting each level/layout in its own activity. You live and you learn I guess. Below is my XML and java. It may not be the elegant solution but I'm not sure I care all that much as long as it works.
<ImageButton android:id="#+id/closeButton"
android:layout_height="wrap_content"
android:background="#drawable/close"
android:layout_width="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp"
android:onClick="closetomain">
</ImageButton>
And here's my Java:
public void closetomain(View view) {
switch(view.getId()) {
case R.id.closeButton:
setContentView(R.layout.main);
break;
}
}
Why do you need to retrieve the id of the current view? Is it just so that you can set it's visibility to GONE? If so, there are probably better ways of implementing that same functionality.
Instead of just changing the layout with setContentView(), it would probably be a better idea to have the Start Screen be its own separate activity. When you are in any of the "maze views" you could simply use an intent to start your home screen activity like this
Intent startScreenIntent = new Intent(this, StartScreen.Class);
startActivity(startScreenIntent);
Then you won't have to worry about finding id's or changing visibilities, plus the code is cleaner, because it separates the code for your Maze levels and your Start Menu.

How to overlay a button programmatically?

What I would like to accomplish is to, at runtime, place a button in the middle of the screen, as the very top layer, overlaying anything below it. (It's not big, so it will not completely cover the screen, just whatever happens to be below it.)
I looked at creating a custom dialog, however that blocks all other user input. I want all of the views below this new button to act normally and respond to the user, but I just want to add (and later remove) the button above everything.
Hopefully that makes sense. I'm just wondering what might be the best approach to look into?
Use a FrameLayout, with the button as it's 2nd child. Set it to GONE when you don't want it visible.
I had to overlay a simple layout programmatically on top of any visible activity. Normal activity layout xmls don't know anything about the overlay. Layout had one textview component but could have any structure you see fit. This is my overlay layout.
res/layout/identity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/identitylayout"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" >
<TextView
android:id="#+id/identityview"
android:padding="5dp"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:textColor="#FFFFFF" android:background="#FF6600"
android:textSize="30dp"
/>
</RelativeLayout>
Overlay is shown on top of the existing content, after timeout is deleted from the screen. Application calls this function to display overlay.
private void showIdentity(String tag, long duration) {
// default text with ${xx} placeholder variables
String desc = getString(R.string.identity);
desc = desc.replace("${id}", reqId!=null ? reqId : "RequestId not found" );
desc = desc.replace("${tag}", tag!=null ? tag : "" );
desc = desc.trim();
// get parent and overlay layouts, use inflator to parse
// layout.xml to view component. Reuse existing instance if one is found.
ViewGroup parent = (ViewGroup)findViewById(R.id.mainlayout);
View identity = findViewById(R.id.identitylayout);
if (identity==null) {
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
identity = inflater.inflate(R.layout.identity, parent, false);
parent.addView(identity);
}
TextView text = (TextView)identity.findViewById(R.id.identityview);
text.setText(desc);
identity.bringToFront();
// use timer to hide after timeout, make sure there's only
// one instance in a message queue.
Runnable identityTask = new Runnable(){
#Override public void run() {
View identity = findViewById(R.id.identitylayout);
if (identity!=null)
((ViewGroup)identity.getParent()).removeView(identity);
}
};
messageHandler.removeCallbacksAndMessages("identitytask");
messageHandler.postAtTime(identityTask, "identitytask", SystemClock.uptimeMillis()+duration);
}
Timer messageHandler is member of main Activity instance (private Handler messageHandler) where I put all scheduled tasks. I am using Android 4.1 device lower than that I don't know what happens.

Categories

Resources