Android: RelativeLayout translateY animation with layout_alignParentBottom - android

I'm working on an Android app with an animation where a vertically central view should be animated from the center to the top of the screen. Below this view is another view containing related content, which is aligned directly below the center view. When the center view animates up, I want the lower view to be 'pinned' to the top but also to the bottom of the screen (as per layout_alignParentBottom="true"). Currently though, as the bottom view animates up a gap is left between the bottom view and the bottom of the screen:
What's the easiest way to animate the bottom view, while keeping it pinned to the bottom of the screen?
Here is my layout XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="newsoni.com.testrelativelayouttranslation.MainActivity">
<View
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="#FF0000"
android:layout_centerVertical="true"
android:id="#+id/centerBar" />
<View
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#00FF00"
android:layout_below="#+id/centerBar"
android:id="#+id/bottomPanel" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Do animation"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:id="#+id/btn" />
</RelativeLayout>
Here is my Java:
public class MainActivity extends AppCompatActivity {
private View mCenter, mBottomPanel;
private Button mBtn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCenter = findViewById(R.id.centerBar);
mBottomPanel = findViewById(R.id.bottomPanel);
mBtn = $(R.id.btn);
mBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int value = mCenter.getTranslationY() == 0 ? -mCenter.getTop() : 0;
mCenter.animate()
.translationY(value)
.setDuration(250)
.start();
mBottomPanel.animate()
.translationY(value)
.setDuration(250)
.start();
}
});
}
#SuppressWarnings("unchecked")
public <T extends View> T $(int id) {
return (T) findViewById(id);
}
}
Here is a link to the project which you can download:
https://drive.google.com/file/d/0B-mqMIMqm_XHamxENkhIZDJDMHM/view?usp=sharing

I ended up using a standard Animation and overriding applyTransformation to manually alter the LayoutParams of the bottom view.

Related

Bottom Sheet dialog landscape width issue

bottom_sheet_image_dialog.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:behavior_hideable="false"
app:behavior_peekHeight="62dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="#+id/llAddDriverPic"
android:background="?android:attr/selectableItemBackground"
android:padding="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="#drawable/baseline_add_photo_alternate_24"
android:layout_margin="8dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/add_picture"
android:layout_gravity="center_vertical"
android:padding="8dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="#+id/llRemoveDriverPic"
android:background="?android:attr/selectableItemBackground"
android:padding="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="#drawable/baseline_no_photography_24"
android:layout_margin="8dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/remove_picture"
android:layout_gravity="center_vertical"
android:padding="8dp"/>
</LinearLayout>
Code implementation:
BottomSheetDialog bsDialog = new BottomSheetDialog(getContext());
bsDialog.setContentView(R.layout.bottom_sheet_image_dialog);
bsDialog.setOnShowListener(dialog -> {
BottomSheetBehavior bottomSheetBehavior = ((BottomSheetDialog)dialog).getBehavior();
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
});
Following issue which occurs: The width, as seen in the video won't appear fully.
If I start the bottomsheet in the beginning in landscape mode the width also looks different as the left and right side are not covered:
What may the issue(s) be?
Is it necessary to predefine the width before showing the dialog?
-> There's no problem with your xml layout, It can be solved in Code. I am providing an example below which solved this issue.
=> Bottom Sheet Class Code
public class BottomSheet extends BottomSheetDialogFragment {
private View view;
private BottomSheetBehavior mBottomBehavior;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.bottom_sheet_layout, container, false);
//Do your logic code here
return view;
}
#Override
public void onStart() {
super.onStart();
mBottomBehavior = BottomSheetBehavior.from((View) view.getParent());
mBottomBehavior.setMaxWidth(ViewGroup.LayoutParams.MATCH_PARENT);
mBottomBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
=> Activity Class Code
Button button = findViewById(R.id.button);
button.setOnClickListener(v -> {
//To open the bottom sheet, NB. make sure 'BottomSheet' is the name you declared Bottom sheet class
BottomSheet bottomSheet = new BottomSheet();
bottomSheet.show(getSupportFragmentManager(), "exampleBottomSheet");
});

Cannot set a click listener for the button that is first outside of the screen (and then animates inside)

I am trying to recreate the edit behavior of iOS list in Android using a RecyclerView. This is the result I want to get (without the delete button):
I don't need the edit button in this context and I am assuming that the little red circle on the left side is always there. When you click the red round button, a rectangle delete button should slide from the right, pushing the row to the left.
What I tried to do was to have a horizontal LinearLayout that is hosting two children. One is a ConstraintLayout that has the main layout of each row and its width is set to match_parent so that it take all the screen width. The other child is the delete button with the width of 130dp which will be outside the screen by default.
Here's the code to the layout file:
<FrameLayout 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:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false">
<LinearLayout
android:id="#+id/root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:clipChildren="false">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="9dp"
android:layout_marginTop="9dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="#+id/remove_circle_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/ic_remove_circle"
android:background="#null"/>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/remove_circle_button"
app:layout_constraintTop_toTopOf="parent"
tools:text="some text" />
</android.support.constraint.ConstraintLayout>
<Button
android:id="#+id/remove_main_button"
style="?android:borderlessButtonStyle"
android:layout_width="130dp"
android:layout_height="match_parent"
android:background="#color/red"
android:stateListAnimator="#null"
android:text="Delete"
android:textColor="#color/white" />
</LinearLayout>
</FrameLayout>
My logic is that when the user clicks the circular red button, the linear layout will animate 130dp to the left making the red button visible while the rest of the row is pushed to the left. Visually it looks good enough for the requirement we have.
Here's the code to the RecyclerView adapter that I wrote:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private Context mContext;
private List<String> mStrings;
private SparseBooleanArray mHasDeleteButtonPressed;
public RecyclerViewAdapter(Context context, List<String> strings) {
this.mContext = context;
this.mStrings = strings;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View row = LayoutInflater.from(mContext).inflate(R.layout.rv_item, parent, false);
return new ViewHolder(row);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, int position) {
String value = mStrings.get(position);
holder.textView.setText(value);
holder.smallDeleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.rootLayout.animate().translationX(
-1 * holder.mainDeleteButton.getWidth())
.setDuration(300)
.start();
mHasDeleteButtonPressed.put(holder.getAdapterPosition(), false);
}
});
holder.mainDeleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext, "clicked", Toast.LENGTH_LONG).show();
}
});
}
#Override
public int getItemCount() {
return mStrings.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
LinearLayout rootLayout;
TextView textView;
ImageButton smallDeleteButton;
Button mainDeleteButton;
ViewHolder(View itemView) {
super(itemView);
rootLayout = (LinearLayout) itemView.findViewById(R.id.root_layout);
textView = (TextView) itemView.findViewById(R.id.time_textView);
smallDeleteButton = (ImageButton) itemView.findViewById(R.id.remove_circle_button);
mainDeleteButton = (Button) itemView.findViewById(R.id.remove_main_button);
}
}
}
The boolean array is to know which button of each row was clicked so that we can animate back the delete button if the user decided they didn't want to delete that row.
Theoretically, with this code the delete button should be pressed and a toast should be shown stating that the button was clicked. However, it appears that the OnClickListener is not being set at all and no code inside the onClick method is being called. My suspicion is that since the button is outside the screen at first, and we are forcing the view not to clip it (using android:clipChildren="false" in the xml file), the setOnClickListener method is not working. Because when I rearrange the layout in a way that the Delete button is inside the screen, the adapter code above will work without any changes.
So how can I fix my problem? I don't want to use any external libraries and I want to do this in a RecyclerView.
Thanks in advance
This is an interesting problem. I can't say what is really going on, but the button that you are sliding onto the screen is being defined outside the bounds of the screen and never really gets onto the screen although it appears to. It's hit rectangle is defined off the screen so the button never gets clicked.
My solution is to first simplify the layout for the RecyclerView items as follows:
activity_main.xml
I have changed a few things such as the button color, etc. for my own ease of use, but they have no bearing on the solution.
<android.support.constraint.ConstraintLayout
android:id="#+id/root_layout"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_marginBottom="9dp"
android:layout_marginTop="9dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="#+id/remove_circle_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:background="#null"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/circle_button" />
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="some text"
android:textColor="#color/colorPrimary"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/remove_circle_button"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/remove_main_button"
style="?android:borderlessButtonStyle"
android:layout_width="130dp"
android:layout_height="0dp"
android:background="#android:color/holo_blue_light"
android:stateListAnimator="#null"
android:text="Delete"
android:textColor="#android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
This layout will show the button on screen. In the activity, the layout will be given a specified width that is the width of the screen plus the width of the button. This re-dimensioning of the layout will move the button off-screen. Code can then do a translation to move it back onto the screen.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private View rootLayout;
private View mainDeleteButton;
private View circleButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rootLayout = findViewById(R.id.root_layout);
mainDeleteButton = findViewById(R.id.remove_main_button);
circleButton = findViewById(R.id.remove_circle_button);
rootLayout.post(new Runnable() {
#Override
public void run() {
rootLayout.getLayoutParams().width = rootLayout.getWidth() + mainDeleteButton.getWidth();
rootLayout.requestLayout();
}
});
circleButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
rootLayout.animate().translationX(
-1 * mainDeleteButton.getWidth())
.setDuration(300)
.start();
}
});
mainDeleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "clicked", Toast.LENGTH_SHORT).show();
}
});
}
}
Here is the effect:
Although this example does not involve a RecyclerView, the technique can be applied to a RecyclerView.

Animate LinearLayout when keyboard goes up/down

I have a layout like this:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="25dp"
android:paddingRight="25dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imageView"
android:background="#drawable/logo" />
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView ... />
<EditText ... />
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView ... />
<EditText ... />
</LinearLayout>
</LinearLayout>
<Button ... />
</LinearLayout>
I want to fade out the ImageView and move the second (inner) LinearLayout up (near the top of the screen) when the keyboard comes up and then do the opposite when the keyboard goes back down.
How can I achieve this?
Use TranslateAnimation class to animate your view.
Animation slide = new TranslateAnimation(fromX,toX,fromY,toY);
slide.setDuration(300);//here you can set your own duration of animation in ms.
yourView.startAnimation(slide);
You can also override some callbacks.
slide.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
There is no direct way of finding out whether android softkey pad is opne or not . But you can do a work around and calculate the size diff between your activity's view root and the window size. There are many ways to do it, one way that i found was this:
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
... do something here
}
}
});

Prevent certain views from being animated with android:animateLayoutChanges="true"

I have a horizontal linear layout that has 3 views. An open and close button and a text view. On load the close button is set to
setVisibility(View.GONE);
So it is only showing one imageview, open, and a textview with some text. I am also using
android:animateLayoutChanges="true"
On my parent layout to animate the textview when it opens and closes. Everything functions right except when I open/close I can see the animation for the two buttons being set to GONE and VISIBLE respectively. I do not want animation on those two ImageViews. Any thoughts on how I can remove the animations on those two views? Thanks in advance.
XML File
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
android:id="#+id/CuFScrollView"
style="#style/ScrollView"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#A0A0A0"
>
<LinearLayout
style="#style/LinearLayoutVertical"
android:paddingTop="20dp"
>
<TextView
android:text="Basic Management Plan for Tactical Field Care"
android:id="#+id/header"
style="#style/Header"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#606060"
android:padding="5dp"
android:animateLayoutChanges="true"
>
<ImageView
android:id="#+id/open"
android:src="#drawable/open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:onClick="open"
android:layout_gravity="center_vertical"
android:animateLayoutChanges="false"
/>
<ImageView
android:id="#+id/close"
android:src="#drawable/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:onClick="close"
android:layout_gravity="center_vertical"
android:animateLayoutChanges="false"
/>
<TextView
android:id="#+id/stepOne"
android:text="Step 1:"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="0dp"
style="#style/Bullet"
/>
</LinearLayout>
</LinearLayout>
Java File
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;
public class Careunderfirestudy extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setContentView(R.layout.careunderfirestudy);
ImageView close = (ImageView)findViewById(R.id.close);
close.setVisibility(View.GONE);
}
public void open(View view){
TextView stepOne = (TextView)findViewById(R.id.stepOne);
stepOne.setText("Casualties should be extricated from burning vehicles or buildings and moved to places of relative safety. Do what is necessary to stop the burning process.");
ImageView close = (ImageView)findViewById(R.id.close);
ImageView open = (ImageView)findViewById(R.id.open);
close.setVisibility(View.VISIBLE);
open.setVisibility(View.GONE);
}
public void close(View view){
TextView stepOne = (TextView)findViewById(R.id.stepOne);
stepOne.setText("Step 1:");
ImageView close = (ImageView)findViewById(R.id.close);
ImageView open = (ImageView)findViewById(R.id.open);
close.setVisibility(View.GONE);
open.setVisibility(View.VISIBLE);
}
}
The question was: Prevent certain views from being animated with android:animateLayoutChanges=“true” and I don't see the answer to this question in the previous answer.
I had the same problem and I solved it disabling temporary the LayoutTransition for appearing and disappearing type.
Here examples.
Hide a View without animating it:
LayoutTransition lt = ((ViewGroup)view.getParent()).getLayoutTransition();
lt.disableTransitionType( LayoutTransition.DISAPPEARING );
view.setVisibility(View.GONE);
lt.enableTransitionType( LayoutTransition.DISAPPEARING );
Show a View without animating it:
LayoutTransition lt = ((ViewGroup)view.getParent()).getLayoutTransition();
lt.disableTransitionType( LayoutTransition.APPEARING );
view.setVisibility(View.VISIBLE);
lt.enableTransitionType( LayoutTransition.APPEARING );
This does not prevent other views to be animated consequently to the view visibility change (they are animated under LayoutTransition.CHANGE_APPEARING and LayoutTransition.CHANGE_DISAPPEARING transition type).
Not null check to LayoutTransition may be done for safety.
You could just use 1 button and change the image:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#606060"
android:padding="5dp"
android:animateLayoutChanges="true"
android:id="#+id/parent"
>
<ImageView
android:id="#+id/toggleStep"
android:src="#drawable/open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:onClick="toggle"
android:layout_gravity="center_vertical"
/>
protected void onCreate(Bundle savedInstanceState) {
...
LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
parent.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
...
}
private boolean mOpen = false;
public void toggle(View view){
TextView stepOne = (TextView)findViewById(R.id.stepOne);
ImageView toggle = (ImageView)findViewById(R.id.toggleStep);
if(mOpen){
stepOne.setText("Step 1:");
toggle.setImageResource(R.drawable.open);
}
else{
stepOne.setText("Casualties should be extricated from burning vehicles or buildings and moved to places of relative safety. Do what is necessary to stop the burning process.");
toggle.setImageResource(R.drawable.close);
}
mOpen = !mOpen;
}

Detect if view is scrolled out of the screen

I have layout with scrollView as below :
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="#+id/transparentLayout"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#android:color/transparent"
android:orientation="vertical" >
</LinearLayout>
... other views
</ScrollView>
I want to detect if the transparentLayout (LinearLayout) is scrolled out of the screen.
I solved it in this fashion:
scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {
#Override
public void onScrollChanged() {
Rect scrollBounds = new Rect();
scrollView.getHitRect(scrollBounds);
if (layout.getLocalVisibleRect(scrollBounds)) {
// if layout even a single pixel, is within the visible area do something
} else {
// if layout goes out or scrolled out of the visible area do something
}
}
});

Categories

Resources