Android Fragment Share Element Transition with Hide and Show - java

I am trying to make Share Element Transition for RecyclerView element , using the image as the share element.
I am able to make other transition work except the Share Element Transition.
my guess is that the problem is on in Transition.hide and transition.Show.
other that that i am stuck. please help :)
Here is the method that handles the transition
public void goToProduct(ProductItem current) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Transition changeTransform = TransitionInflater.from(this).
inflateTransition(R.transition.change_image_transform);
Transition explodeTransform = TransitionInflater.from(this).
inflateTransition(android.R.transition.explode);
productListFragment.setSharedElementReturnTransition(changeTransform);
productListFragment.setExitTransition(explodeTransform);
productPageFragment.setSharedElementEnterTransition(changeTransform);
productPageFragment.setEnterTransition(explodeTransform);
isProductPageOpenedFromCart = false;
isProductPageOpenedFromList = false;
ImageView listImage = (ImageView) findViewById(R.id.listImg1);
ImageView pageImage = (ImageView) findViewById(R.id.pageImg);
FragmentTransaction transaction = manager.beginTransaction();
transaction.hide(productListFragment)
.addToBackStack("transaction")
.addSharedElement(listImage, "MyTransition");
if (isCartOpen) {
CartFragment cartFragment = (CartFragment) manager.findFragmentByTag(CART_FRAGMENT_TAG);
transaction.remove(cartFragment);
isCartOpen = false;
isProductPageOpenedFromCart = true;
} else {
isProductPageOpenedFromList = true;
}
productPageFragment.setProduct(current);
transaction.show(productPageFragment).addToBackStack("transaction")
.addSharedElement(pageImage, "MyTransition");;
transaction.commit();
}
else {
the transition folder file is named change_image_transform and contains:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeTransform/>
<changeImageTransform/>
</transitionSet>
I also did use android:transitionName="MyTransition" on boath images.
And here are lines of code added to style.xml
<item name="android:windowContentTransitions">true</item>
<item name="android:windowEnterTransition">#transition/change_image_transform</item>
<item name="android:windowExitTransition">#transition/change_image_transform</item>
<item name="android:windowSharedElementEnterTransition">#transition/change_image_transform</item>
<item name="android:windowSharedElementExitTransition">#transition/change_image_transform</item>

In RecyclerView you propably have many views with id R.id.listImg1 You have to in adapter set sharedView setTransitionName programatycally for example you can use your product id for creating transition name. And when you creating second details fragment you can pass by bundle transition name to that gragment. In onCrete you can set image transitionname to the same like before.
Or you can create:
public interface IRecyclerClikListener {
public void onItemClik(int pos,Object obj,View view);
}
Add this listener to your recycler. To the view you pass a refrence to clikced row. If you have reference to cliked view you can set transition name to image belongs to this view. Dont forget to set the same transition name in second fragment.

Related

How to use same setOnClickListener for mutliple functions in Kotlin?

I have this setOnClickListener that first check if the user logged in then if the user logged in will run the function as below:
like.setOnClickListener {
val sharedPreference2 = context?.getSharedPreferences("isLogin", Context.MODE_PRIVATE)
val fbtoken = sharedPreference2?.getString("UserToken", "false")
if(fbtoken.equals("false"))
{
val builder = AlertDialog.Builder(context).create()
val optionDialog = AlertDialog.Builder(context)
val layoutInflator = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val dialogView = layoutInflator.inflate(R.layout.alert_reg, null)
val goreg = dialogView.findViewById<TextView>(R.id.confirmation_reg)
val cancel = dialogView.findViewById<TextView>(R.id.cancel_reg)
builder.setCancelable(false)
builder.setView(dialogView)
goreg?.setOnClickListener {
val intent = Intent(context, SignupActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent)
}
cancel?.setOnClickListener {
builder.dismiss()
}
builder.show()
}
else
{
postLike(position, it.context)
like.setImageResource(R.drawable.liked)
}
}
So the this setOnClickListener first will check the login and in "else" will post in API.
I'm trying to use same setOnClickListener used above if the user click again , it will delete it from API
I tried to replace "else" with "if" and "else" like the code below:
if (fbtoken.equals("true")){
postLike(position, it.context)
like.setImageResource(R.drawable.liked)
}else{
like.setImageResource(R.drawable.like)
deleteLike(position, it.context)
}
But now it delete it from API only
Its something similar to IG like button if I click first time the button it will post the like and if I click again it will delete it.
What am I doing wrong here?
Correct way would be, not to call setImageResouce everytime user clicks on the image but have selector which would have two states like this
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/ic_address_item_bg"android:state_selected="false"/>
<item android:drawable="#drawable/ic_selected_address_bg"
android:state_selected="true"/>
<item android:drawable="#drawable/ic_selected_address_bg" />
</selector>
you should set this xml as src parameter of image view or whataver view it is .
after that you should change state of view respectively according to user action
likeButton.setonCLickListener {
it.isSelected = !it.isSelected
if (it.isSelected) {
postLike()
} else {
postUnlike()
}
}
You are not changing the value saved in the preferences. You need to update the value to true false as clicks of button.
if (fbtoken.equals("true")){
postLike(position, it.context)
like.setImageResource(R.drawable.liked)
fbtoken = sharedPreference2?.putString("UserToken", "false")
}else{
like.setImageResource(R.drawable.like)
deleteLike(position, it.context)
fbtoken = sharedPreference2?.putString("UserToken", "true")
}

change tint of button programmatically

I simply want to change the backgroundTint of a normal button from inside the java code. I tried many different approaches like ColorStateList or setColorFilter, but nothing worked. I am purposefully not using setBackgroundColor since I want to keep the original shape of the button.
Furthermore, the colors I want to use are already defined in my resources. After lots of trial and error I managed to access these colors with this code:
int colorBtnDeactivated = ContextCompat.getColor(this, R.color.colorBtnDeactivated);
So basically I only need this one line of java code which enables me to access the background tint. The rest I can do myself.
I would really appreciate help, I have been stuck on this problem for hours. Thanks!
Edit: Using a selector-xlm didn't work, since it only changed the color of the button while being pressed. Also the buttons will influence each other, so by pressing one button I will need to be able to change the background tint of another button.
Edit 2: I tried again with setColorFilter:
//this is all inside the onCreate-method
int colorBtnActiveTest= ContextCompat.getColor(this, colorBtnActive);
int colorBtnDeactivatedTest=ContextCompat.getColor(this, colorBtnDeactivated);
Button btnKnockOne = (Button)findViewById(R.id.btnKnockOne);
boolean stateBtnKnockOne = false;
btnKnockOne.getBackground().setColorFilter(colorBtnDeactivatedTest, PorterDuff.Mode.SRC_IN);
btnKnockOne.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (stateBtnKnockOne==false){
btnKnockOne.getBackground().setColorFilter(colorBtnActiveTest, PorterDuff.Mode.SRC_IN);
stateBtnKnockOne=true;
}
else if (stateBtnKnockOne==true){
btnKnockOne.getBackground().setColorFilter(colorBtnDeactivatedTest, PorterDuff.Mode.SRC_IN);
stateBtnKnockOne=false;
}
}
});
This is the result:
When I open the activity, the button is displayed in the default grey button color, not in my custom color colorBtnDeactivatedTest
When I press the button, it briefly changes its color to colorBtnActiveTest, but then goes back to its grey color
I finally found a solution! This post helped me find it: https://stackoverflow.com/a/8748112/8952749
Now I basically have two buttons of which only one can be selected at one time. For this to work I created a selector-xlm:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:drawable="#drawable/button_active" />
<item
android:state_selected="false"
android:drawable="#drawable/button_deactivated" />
</selector>
This is the java code, which enables me to change state_selected of the buttons:
stateBtn1=false;
stateBtn2=false;
btnTest1=(Button)findViewById(R.id.button);
btnTest2=(Button)findViewById(R.id.button2);
btnTest1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (stateBtn1==false){
btnTest1.setSelected(true);
stateBtn1=true;
btnTest2.setSelected(false);
stateBtn2=false;
}
else if (stateBtn1==true){
btnTest1.setSelected(false);
stateBtn1=false;
}
}
});
btnTest2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (stateBtn2==false){
btnTest2.setSelected(true);
stateBtn2=true;
btnTest1.setSelected(false);
stateBtn1=false;
}
else if (stateBtn2==true){
btnTest2.setSelected(false);
stateBtn2=false;
}
}
});

Android: checking a view's background

I have 3 buttons, all of them have the same background drawable by default (subject_button)
What I want to do:
When I click one button his background changes ( to clicked_subject), all the others remain with the default background, if I click one button after clicking another, the button I just clicked changes his background while the previous one gets back to the initial background, allowing only one button to have the clicked_subject background, if the diffrent button gets clicked again his background goes back to the initial one, leting all the buttons with the initial background.
The problem:
If I click the diffrent button, his background remains the same instead of changing back to the initial one.
My logic:
theButton1.setBackgroundResource(R.drawable.subject_button);
theButton1.setOnClickListener(this);
//same for other 2
#Override
public void onClick(View v) {
if (v.getBackground() == ContextCompat.getDrawable(this, R.drawable.subject_button)) {
theButton1.setBackgroundResource(R.drawable.subject_button);
theButton2.setBackgroundResource(R.drawable.subject_button);
theButton3.setBackgroundResource(R.drawable.subject_button);
v.setBackgroundResource(R.drawable.clicked_subject);
} else {
v.setBackgroundResource(R.drawable.subject_button);
}
Why is this happening?
You can do this:
private final List<Button> mButtons = new ArrayList<>();
// somewhere in your code
mButtons.add(mButton1);
mButtons.add(mButton2);
mButtons.add(mButton3);
mButton1.setOnClickListener(mClickListener);
mButton2.setOnClickListener(mClickListener);
mButton3.setOnClickListener(mClickListener);
private final View.OnClickListener mClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
for (Button button : mButtons) {
if (button == v) {
// set selected background
} else {
// set not selected backround
}
}
}
};
If you define a stateful drawable for your buttons, then you can simply change the onclick to this:
private final View.OnClickListener mClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
for (Button button : mButtons) {
button.setSelected(v == button);
}
}
};
For changing background color/image based on the particular event(focus, press, normal), you need to define a button selector file and implement it as background for button. For example button_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="#drawable/your_image1" /> <!-- pressed -->
<item android:state_focused="true"
android:drawable="#drawable/your_image2" /> <!-- focused -->
android:drawable="#drawable/your_image3" <!-- default -->
</selector>
then just apply it as :
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawable="#drawable/button_selector.xml" />
I solved it by instead of changing only the background color on clicking the button, I also change the textColor, then I can check the textColor with if (((Button) v).getCurrentTextColor() == Color.WHITE)
"Pressed", "selected", "disabled" and such are View states. As such they're supposed to be handled automatically by Android and not by your click listeners.
This is achieved using SateLists, which control the look your Views sould have depending on which state(s) they're in. So you can easily set subject_button as the unpressed state, clicked_subject as the pressed state, and let Android take care of actually switching between them.
Full explanation: https://developer.android.com/guide/topics/resources/drawable-resource.html#StateList

Disallow selection of some ListView items, but still get click events?

I'm using a simple ListView for my navigation drawer, with android:choiceMode="singleChoice" set in the layout XML. This allows me to use a selector for the text color:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:color="#color/primary_500" />
<item android:color="#color/text_primary" />
</selector>
Which will color the selected item with primary_500 and the rest with text_primary.
The above is working fine, but one of the items will launch a new activity instead of loading a fragment, so it should never be made the ListView's selected item.
I've tried reverting it with this:
public void selectItem( int index ) { selected_item = index; }
public void revertSelectedItem() {
int y = navigation.getScrollY();
list.setSelection( selected_item );
list.scrollTo( 0, y );
}
And elsewhere:
#Override
public void onItemClick( ListView list, View view, int index, long id ) {
switch( (int)id ) {
case R.id.exampleItem_forFragment:
/* Swap fragments */
selectItem( index );
break;
case R.id.exampleItem_forActivity:
/* Launch activity */
revertSelectedItem();
break;
}
}
But, even though revertSelectedItem() is called, the selection does not change. I'm presuming that the new selection is not assigned until after onItemClick() returns, so my change is getting overwritten.
Is there a clean way to disable or revert selection changes for only certain items, but still receive click events?
Seems I just misunderstood the purpose of some methods. Changing revertSelectedItem() to look like this:
public void revertSelectedItem() {
list.setItemChecked( selected_item, true );
}
behaves exactly like I want. It looks like ListView uses the item's "checked" state, rather than the selection, to decide which items are marked. When in choiceMode="singleChoice", calling setItemChecked() also unchecks all other items automatically.

Android TransitionDrawable with multiple items

I want to create a Button in Android with a text, and a background image. The background image should crossfade every X time.
I have this working using a TransitionDrawable with 2 images.
But I can't get this to work with more than 2 images.
What I have :
In Java code I create a Button and set a background (which is a TransitionDrawable defined in XML). And I start the transition.
final Button b = new Button(getApplicationContext());
b.setTextColor(getResources().getColor(R.color.white));
b.setText("Some text");
b.setBackgroundDrawable(getResources().getDrawable(R.drawable.tile));
StateListDrawable background = (StateListDrawable) b.getBackground();
TransitionDrawable td = (TransitionDrawable) background.getCurrent();
td.startTransition(2000);
In XML I define in tile.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" >
<shape>
<solid
android:color="#449def" />
</shape>
</item>
<item android:drawable="#drawable/transition">
<shape>
<solid
android:color="#0000ff" />
</shape>
</item>
</selector>
And finally a transition.xml
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:drawable="#drawable/desert"/>
<item android:drawable="#drawable/hydrangeas" />
<item android:drawable="#drawable/jellyfish" />
</transition>
Now the effect is that when I start the app the desert image is shown. This image crossfades to the hydrangeas image as it should. But the jellyfish image is never shown.
In the doc for TransitionDrawables it is stated that you can specify more than 2 drawables but I can't get this to work.
I also tried this without any XML but in pure JAVA but this gave exactly the same problem :-(
You can do it by using a handler
mAnimateImage is your button
int DrawableImage[] = {R.drawable.back_red, R.drawable.back_green, R.drawable.back_purple};
final Handler handler = new Handler();
final int[] i = {0};
final int[] j = {1};
handler.postDelayed(new Runnable() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
Resources res = getApplicationContext().getResources();
TransitionDrawable out = new TransitionDrawable(new Drawable[]{res.getDrawable(DrawableImage[i[0]]), res.getDrawable(DrawableImage[j[0]])});
out.setCrossFadeEnabled(true);
mAnimateImage.setBackgroundDrawable(out);
out.startTransition(4000);
i[0]++;
j[0]++;
if (j[0] == DrawableImage.length) {
j[0] = 0;
}
if (i[0] == DrawableImage.length) {
i[0] = 0;
}
handler.postDelayed(this, 8000);
}
});
}
}, 0);
According to the official documentation, TransitionDrawable can only cross-fade among 2 layers, quoting from the official android reference.
An extension of LayerDrawables that is intended to cross-fade between
the first and second layer. To start the transition, call
startTransition(int). To display just the first layer, call
resetTransition().
If you don't read it carefully, since it extends LayerDrawables, which can have multiple layers, one may expect that you could cross-fade from N layers. But it is very clear, startTransition shows the second layer, resetTransition shows the first.
I suggest you do your own implementation for multiple transitions. What I'd do is to have 2 images and animate them. You may need to set the drawables by hand, but it should be a quite simple piece of code.
in appendix operating time you can dynamically change pictures
Use td.setDrawableByLayerId(td.getId(1), drawable) on your TransitionDrawable
TransitionDrawable transitionDrawable = (TransitionDrawable) myImage
.getDrawable();
transitionDrawable.setDrawableByLayerId(transitionDrawable.getId(1), getResources()
.getDrawable(R.drawable.c));
You can only transition maximum two images with TransitionDrawable. To work with more than two images you can extend LayerDrawable and implement your own TransitionDrawable.
Here's the ready implementation of custom TransitionDrawable to work with more than two images.
You can see the complete sample along with the gif demo here on Github.
No need to use handler, it's more efficient + cleaner to use coroutines instead of a handler and as a bonus you can make this transition between as many drawables as you like:
/**
* Run multitransition animation on a view.
*
* #param drawableIds The drawables to load into the view, one after the other
* #param delayBetweenTransitions the number of milliseconds to transition from one drawable to the next
* #param viewToAnimate The view to animate.
*/
fun runMultitransitionAnimation(#DrawableRes vararg drawableIds: Int,
delayBetweenTransitions : Int,
viewToAnimate : View) {
CoroutineScope(Dispatchers.Main).launch(Dispatchers.Main.immediate) {
val delay = SOME_INTEGER_MILLISECONDS
for (i in 0 until drawableIds.size-1) {
val nextTransition = TransitionDrawable(arrayOf(
context.getDrawable(drawableIds[i]),
context.getDrawable(drawableIds[i+1])))
))
nextTransition.isCrossFadeEnabled = true
viewToAnimate.background = nextTransition
nextTransition.startTransition(delayBetweenTransitions)
delay(delayBetweenTransitions*2)
}
}
}
You'll need to just make a new TransitionDrawable per iteration.
This will step up through your index of all the images you add, you can have any number of images...
private Handler handler = new Handler();
private void startImageCrossfading(#IdRes int imageViewResId, #DrawableRes int... drawableIds) {
Drawable[] bitmapDrawables = new Drawable[drawableIds.length];
for (int i = 0; i < drawableIds.length; i++) {
// if you are using Vectors cross fade won't work unless you do this! - See my answer at https://stackoverflow.com/a/54583929/114549
// bitmapDrawables[i] = getBitmapDrawableFromVectorDrawable(this, drawableIds[i]);
bitmapDrawables[i] = ContextCompat.getDrawable(this, drawableIds[i]);
}
final ImageView imageView = findViewById(imageViewResId);
handler.postDelayed(new Runnable() {
int index = 0;
#Override
public void run() {
TransitionDrawable td = new TransitionDrawable(new Drawable[]{
bitmapDrawables[index % bitmapDrawables.length],
bitmapDrawables[++index % bitmapDrawables.length]
});
td.setCrossFadeEnabled(true);
imageView.setImageDrawable(td);
td.startTransition(500); // transitionDurationMillis
handler.postDelayed(this, 3000);
}
}, 3000);
}
On pause you should clean up the handler
handler.removeCallbacksAndMessages(null);

Categories