I have researched the questions thoroughly, but could not yet find the answer. Also, my excuses for my poor english since I am not a native speaker.
The problem: in my android layout we have a status_text with a listview below the status_text. When the status_text is touched, we animate a 'move down' on the status_text and listview so that only the first of the listview row is still on screen. The listview is now still usable.
When the status_text is touched again, we move the status_text and listview up so that the listview uses half of the screen.
The problem we are facing is that during the 'move up' only the first row is animated. After the 'move up' the other rows suddenly appear.
What we would like to have is a 'move up' where the previously hidden rows slide onto the screen.
The layout:
We are using this layout (slightly simplified to focus on the problem at hand):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_declareren_choose_verzekerden"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Dummy anchor to put top of listview in the middle of the screen -->
<RelativeLayout
android:id="#+id/anchor"
style="#style/anchor_status_container"
android:layout_centerVertical="true" >
</RelativeLayout>
<!-- Example image -->
<ImageView
android:id="#+id/my_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/footer"
android:adjustViewBounds="true"
android:contentDescription="#string/image_description"
android:scaleType="centerCrop"
android:src="#drawable/empty" />
<!-- Clickable text which moves up and down on click -->
<RelativeLayout
android:id="#+id/status_container"
style="#style/status_container"
android:layout_alignTop="#id/anchor"
android:background="#color/white" >
<TextView
android:id="#+id/status_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="#dimen/spacing_sml"
android:layout_alignTop="#id/status_container" />
</RelativeLayout>
<!-- Listview which moves up and down with the status_container -->
<RelativeLayout
android:id="#+id/listView_container"
style="#style/padding_content_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/status_container"
android:background="#color/white" >
<ListView
android:id="#+id/mylistView"
style="#style/myListviewStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:divider="#null"
android:dividerHeight="0dp" />
</RelativeLayout>
<!-- Footer with buttons -->
<RelativeLayout
android:id="#+id/footer_button_container"
style="#style/footer"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="#+id/button_again"
style="#style/btn_secondary"
android:layout_width="wrap_content"
android:layout_alignParentLeft="true"
android:text="#string/opnieuw"
android:visibility="gone" />
<Button
android:id="#+id/button_next"
style="#style/btn_primary"
android:layout_width="wrap_content" />
</RelativeLayout>
</RelativeLayout>
And the code (again a bit simplified to show only the problem at hand. Some fade-in/out and rotations are removed):
// The code
#Override
public void onClick(View view)
{
int viewId = view.getId();
if (viewId == R.id.status_container)
{
// Someone clicked the text, move the statusbar (and so the listview) up or down
if (this.viewIsInUpperPosition)
{
startStatusAnimation();
}
}
}
private void startStatusAnimation()
{
if (animationIsRunning)
{
return;
}
setAnimationIsRunning(animValues.START);
// 0. Initialisation
final View statusContainer = (View) getView().findViewById(R.id.status_container);
final View listContainer = (View) getView().findViewById(R.id.listView_container);
final ListView listView = (ListView) getView().findViewById(R.id.myListView);
final View footerButtonContainer = (View) getView().findViewById(R.id.footer_button_container);
// 1. Calculate distance for animation
if (toggleViewDistance == 0)
{
int listViewContainerHeight = listContainer.getHeight();
int footerHeight = footerButtonContainer.getHeight();
int spaceForListView = listViewContainerHeight - footerHeight;
toggleViewDistance = spaceForListView;
}
// 2. Decide if the movement is up or down
float translationDistance = (viewIsInUpperPosition) ? toggleViewDistance : 0 - toggleViewDistance;
// 3. Create the animation
TranslateAnimation yMove = new TranslateAnimation(0, 0, 0, translationDistance);
yMove.setDuration(animValues.ANIMATION_Y_DURATION);
yMove.setInterpolator(new AccelerateDecelerateInterpolator());
// Do here something with scaling and rotating of other objects, not relevant for the question on StackOverflow
// 4. Actions after animation
yMove.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationEnd(Animation arg0)
{
// Fade de listView in als je van onderen naar boven animeert
if (!viewIsInUpperPosition)
{
// Do some fading, outside scope of question
}
// Create layout after the animation
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) statusContainer.getLayoutParams();
if (viewIsInUpperPosition)
{
// View was previously in upper position, now put the statusbar aligned with the footer
params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.ABOVE, footerButtonContainer.getId());
}
else
{
// View was previously in bottom position, so put it under the anchor
params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.ALIGN_TOP, R.id.anchor);
}
}
statusContainer.setLayoutParams(params); // Set the new layout params
viewIsInUpperPosition = !viewIsInUpperPosition;
setAnimationIsRunning(animValues.END);
}
#Override
public void onAnimationRepeat(Animation arg0)
{
// Empty
}
#Override
public void onAnimationStart(Animation arg0)
{
// empty
}
});
// 5. Start the animation
statusContainer.startAnimation(yMove);
listContainer.startAnimation(yMove);
}
Any advice on how to have the rows of the listview 'slide in' on the screen? Much appreciated!
I figured it out. So I am answering my own question in case someone stumbles upon this question.
What needs to be done is that the listview is drawn off-screen. This can be forced by calling the measure- and layout-methods with the off-screen coordinates of the listview.
This fixed it for my code:
// 5a. Draw the listview off-screen
if (translationDistance < 0)
{
// Do this only when the listview is sliding up, e.g. sliding the window in.
int listViewContainerVerticalPos = listContainer.getTop(); // De positie van de listview
// The required height of the listview
int listContainerHeight = (int) Math.abs(translationDistance) + statusContainer.getHeight();
int measureWidth = View.MeasureSpec.makeMeasureSpec(listContainer.getWidth(), View.MeasureSpec.EXACTLY);
int measureHight = View.MeasureSpec.makeMeasureSpec(listContainerHeight, View.MeasureSpec.EXACTLY);
listContainer.measure(measureWidth, measureHight);
listContainer.layout(0, listContainerVerticalPos, listContainer.getMeasuredWidth(), listContainerVerticalPos
+ listContainerHeight);
}
Related
I am working on an android project where I have a custom view. When the custom view is clicked, I want a to put a view (a circle) at each corner of the view.
At the moment I'm just trying to get it work in the top left corner but it ends up in the middle.
Below is my click function for adding the view.
View view = LayoutInflater.from(getContext()).inflate(R.layout.view, this, false);
TextView textItem = view.findViewById(R.id.lblItemText);
textItem.setText("View: " + counter);
view.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Anchor anchor1 = new Anchor(getContext());
anchor1.setLeft(v.getLeft());
anchor1.setTop(CustomView.this.getTop());
CustomView.this.addView(anchor1);
}
});
The custom view is hosted inside a relative layout. The custom view extends RelativeLayout and the anchor view which is supposed to go into the top left corner of the custom view extends button.
The anchor constructor contains the following:
public Anchor(Context context)
{
super(context);
this.setBackgroundResource(R.drawable.anchor);
this.setPadding(0,0,0,0);
this.setWidth(1);
this.setHeight(1);
}
For some reason the anchor is appearing in the middle instead of being on the corner as shown below
Below is kind of expecting.
UPDATE
After a couple of days made some progress and I do have it working, except its using hardcoded values to get it in the right position, which doesn't seem right. I'm guessing this will only work on the specific device I'm testing on, another device with another resolution will be positioned wrong.
Below is the code I have that hopefully shows what is I am trying to achieve along with a screenshot as to what I have now.
private void createAnchorPoints()
{
//Main View
ViewGroup mainView = activity.findViewById(android.R.id.content);
int[] viewToBeResizedLoc = new int[2];
viewToBeResized.getLocationOnScreen(viewToBeResizedLoc);
//Add top left anchor
Anchor topLeftAnchor = new Anchor(context, Anchor.ResizeMode.TOP_LEFT);
FrameLayout.LayoutParams topLeftParms = new FrameLayout.LayoutParams(150,150);
topLeftParms.leftMargin = viewToBeResizedLoc[0] - 50;
topLeftParms.topMargin = viewToBeResizedLoc[1] - viewToBeResized.getHeight() - 30;
topLeftAnchor.setLayoutParams(topLeftParms);
mainView.addView(topLeftAnchor);
//Add top right anchor
Anchor topRightAnchor = new Anchor(context, Anchor.ResizeMode.TOP_RIGHT);
FrameLayout.LayoutParams topRightParms = new FrameLayout.LayoutParams(150, 150);
topRightParms.leftMargin = topLeftParms.leftMargin + viewToBeResized.getWidth() - 40;
topRightParms.topMargin = topLeftParms.topMargin;
topRightAnchor.setLayoutParams(topRightParms);
mainView.addView(topRightAnchor);
//Add bottom left anchor
Anchor bottomLeftAnchor = new Anchor(context, Anchor.ResizeMode.BOTTOM_LEFT);
FrameLayout.LayoutParams bottomLeftParms = new FrameLayout.LayoutParams(150, 150);
bottomLeftParms.leftMargin = topLeftParms.leftMargin;
bottomLeftParms.topMargin = topLeftParms.topMargin + viewToBeResized.getHeight() - 40;
bottomLeftAnchor.setLayoutParams(bottomLeftParms);
mainView.addView(bottomLeftAnchor);
//Add bottom right anchor
Anchor bottomRightAnchor = new Anchor(context, Anchor.ResizeMode.BOTTOM_RIGHT);
FrameLayout.LayoutParams bottomRightParms = new FrameLayout.LayoutParams(150, 150);
bottomRightParms.leftMargin = topRightParms.leftMargin;
bottomRightParms.topMargin = bottomLeftParms.topMargin;
bottomRightAnchor.setLayoutParams(bottomRightParms);
mainView.addView(bottomRightAnchor);
}
Since the top-level layout is a RelativeLayout, you will need to use the view positioning that is available to RelativeLayout to achieve what you want. (See the documentation.)
Here is a mock-up of what you want to achieve in XML. This mock-up will demonstrate how we can approach the actual solution. I am using standard views, but it shouldn't matter. The technique will apply to your custom views. The image is from Android Studio's designer, so no code was used to create the image.
activity_main.xml
<RelativeLayout
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout
android:id="#+id/customView"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerInParent="true"
android:background="#android:color/holo_green_light" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignStart="#id/customView"
android:layout_alignTop="#id/customView"
android:src="#drawable/circle"
android:translationX="-10dp"
android:translationY="-10dp" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignEnd="#id/customView"
android:layout_alignTop="#id/customView"
android:src="#drawable/circle"
android:translationX="10dp"
android:translationY="-10dp" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignBottom="#id/customView"
android:layout_alignStart="#id/customView"
android:src="#drawable/circle"
android:translationX="-10dp"
android:translationY="10dp" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignBottom="#id/customView"
android:layout_alignEnd="#id/customView"
android:src="#drawable/circle"
android:translationX="10dp"
android:translationY="10dp" />
</RelativeLayout>
circle.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!-- fill color -->
<solid android:color="#android:color/holo_red_light" />
<size
android:width="20dp"
android:height="20dp" />
</shape>
The Actual Solution
Now that we have demonstrated that the mocked-up approach works, we now have to reproduce the effect in code. We will have to add the circle view and position it within the parent RelativeLayout using RelativeLayout view positioning and translations. The following code shows just the top left circle positioned, but the other circles will be positioned in a similar way.
activity_main.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Drawable circle = ContextCompat.getDrawable(this, R.drawable.circle);
ImageView imageView = new ImageView(this);
imageView.setImageDrawable(circle);
int circleSize = dpToPx(CIRCLE_SIZE_DP);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(circleSize, circleSize);
// Position top left circle within the custom view.
lp.addRule(RelativeLayout.ALIGN_START, R.id.customView);
lp.addRule(RelativeLayout.ALIGN_TOP, R.id.customView);
// Uncomment these 2 lines to position the top left circle with translation.
imageView.setTranslationX(-circleSize / 2);
imageView.setTranslationY(-circleSize / 2);
// Uncomment these 3 lines to position the top left circle with margins.
// View customView = findViewById(R.id.customView);
// lp.leftMargin = customView.getLeft() - circleSize / 2;
// lp.topMargin = customView.getTop() - circleSize / 2;
((RelativeLayout) findViewById(R.id.relativeLayout)).addView(imageView, lp);
}
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
private static final int CIRCLE_SIZE_DP = 20;
}
The code above uses a shortened layout:
activity_main.xml
<RelativeLayout
android:id="#+id/relativeLayout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout
android:id="#+id/customView"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerInParent="true"
android:background="#android:color/holo_green_light" />
</RelativeLayout>
It is also possible to produce the same positioning using margins. The code to use margins is commented out but will work. (I think that negative margins may also work, but I have read that they are not officially supported, so I try to avoid them.)
I have 3 view objects(essentially ImageView's) with in a LinearLayout that in turn is within a RelativeLayout , The views are aligned within the LinearLayout as follows:
1 - 2 - 3 - -
The 3 views are wrapped under the Linear layout as demonstrated above.
Basically the idea is to get the side views(1st and 3rd views) move with animation to the place of the middle view onClick() .(so if 3rd view is clicked it should move to middle view's place and the middle moves right to the 3rd view's place.)
I implemented the translate animation as such that the 3rd view when clicked over 100dp to left and 2nd view moves to right, happens fine, but when I apply the translate animation on the onClick() of 1st view to make it move to right and 2nd to move to left then the 2nd and 3rd views both move collectively!! , This means the views are mapped next to each other automatically by the Android!!(note: that's why I used a LinearLayout so that alignment problems don't occur like 2nd view is aligned_below 1st view and aligned_left of 3rd view problems. )
also another problem is that though the views make a move land on another position that was originally belonging to the other view before the animated movement they still maintain the original view ID ?? For.Example that when 3rd view moves left to 2nd views place and 2nd mvoes to 3rd views place the onClick() still is mapped to their original location's?
XML file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</LinearLayout>
<RelativeLayout
android:id="#+id/Container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#138AF8" >
<RelativeLayout
android:id="#+id/RelativeLayout01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="#138AF8" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:orientation="horizontal" >
<ImageView
android:id="#+id/ImageView01"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/cost" />
<ImageView
android:id="#+id/imageView1"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/cost" />
<ImageView
android:id="#+id/ImageView02"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/cost" />
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
Those 3 ImageViews with the names of ImageView1, ImageView01, ImageView02 are the one's I'm trying to animate.
Animation code:
final TranslateAnimation moveLefttoRight = new TranslateAnimation(0,
100, 0, 0);
final TranslateAnimation moveRightToLeft = new TranslateAnimation(0,
-100, 0, 0);
moveRightToLeft.setDuration(1000);
moveLefttoRight.setDuration(1000);
moveRightToLeft.setFillAfter(true);
moveLefttoRight.setFillAfter(true);
final ImageView image1 = (ImageView) findViewById(R.id.imageView1);
final ImageView image2 = (ImageView) findViewById(R.id.ImageView01);
final ImageView image3 = (ImageView) findViewById(R.id.ImageView02);
image1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
Toast.makeText(getApplicationContext(), "image1 clicked",
Toast.LENGTH_SHORT).show();
}
});
image2.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
Toast.makeText(getApplicationContext(), "image2 clicked",
Toast.LENGTH_SHORT).show();
image2.startAnimation(moveLefttoRight);
image1.startAnimation(moveRightToLeft);
}
});
image3.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
Toast.makeText(getApplicationContext(), "image3 clicked",
Toast.LENGTH_SHORT).show();
image2.startAnimation(moveLefttoRight);
image3.startAnimation(moveRightToLeft);
}
});
Translate animation doesn't actually move the view, it just makes it look like that. You have to set the params before the end of the animation so that the button moves as well.example
with the other issue can you post your xml layout file? do you use weights in the imageviews?
I ended up using ObjectAnimator builtin animating API and this moves the view's position permanently to the co-ords. requested.
I have four linear layouts in my screen.
The first layout contain a textview.
I'm trying to move my textView to the the fourth layout of the right with a translate animation.
But when i do that the text view move behind the other layout and if i move my layout from the fourth layout of the right to the first at the left it's ok.
Im my xml i have put : in all layouts
android:clipChildren="false"
image
Can you help me ?
Thank you
Use setZAdjustment to put your View in front of the other Views.
http://developer.android.com/reference/android/view/animation/Animation.html#setZAdjustment%28int%29
Pre-Kitkat :
yourLayout.bringToFront();
((View)yourLayout.getParent()).requestLayout();
((View)yourLayout.getParent()).invalidate();
KitKat :
yourLayout.bringToFront();
Android linear layout construction starts from first element from the beginning. So any element defined first will be created and then rest, so no matter what you do, you cannot achieve with linear layout. Try with relative layout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/testAnimTranslate"
android:layout_width="wrap_content"
android:layout_height="80dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:gravity="center_vertical"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="30dp"
android:layout_height="70dp"
android:background="#0000dd"
android:orientation="vertical" />
<LinearLayout
android:layout_width="30dp"
android:layout_height="70dp"
android:layout_alignParentBottom="true"
android:background="#0dd0dd"
android:orientation="vertical" />
<LinearLayout
android:layout_width="30dp"
android:layout_height="70dp"
android:layout_alignParentBottom="true"
android:background="#ddd0dd"
android:orientation="vertical" />
<LinearLayout
android:layout_width="30dp"
android:layout_height="70dp"
android:layout_alignParentBottom="true"
android:background="#44d0dd"
android:orientation="vertical" />
</LinearLayout>
<TextView
android:id="#+id/textAnimate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#id/testAnimTranslate"
android:layout_alignTop="#id/testAnimTranslate"
android:layout_toLeftOf="#id/testAnimTranslate"
android:gravity="center"
android:background="#00000000"
android:text="qweqwew" />
</RelativeLayout>
Define your translate anim in anim folder or programatically. make sure to add
LinearInterpolator
setFillAfter to true
and start the anim
I think you create the view in the code, so you should add setClipChildren(false)
in your constructor too.
Look at the docs:
ZORDER_TOP: Requests that the content being animated be forced on top of all other content for the duration of the animation.
Please check that setFillAfter(true) does not match this usage.
Does it help?
The main problem with what you are trying to do, is that you want to draw a View outside of its parent. It goes behind the other LinearLayouts because they are drawn after the LinearLayout parent of the View. Even if it is brought to the front, it seems that only relates to children within a single parent?
If you look at how Fragment animations work, you need to recreate the Fragment to translate a from one Frame into another. You also need two separate animations.
BlackBeard's solution will work because it makes the TextView a child of the outermost parent and declares it last. This means the TextView is drawn after everything else and therefore will be drawn on top of everything else.
This doesn't achieve what I think you are trying to do. If you want the TextView to belong to its destination LinearLayout after the animation you'll need to recreate the TextView and add it to the LinearLayout in the correct position in the hierarchy. You'll also need a second animation to move the new TextView into its position.
If done properly the animations should overlay each other perfectly and if in a LinearLayout one or the other of the animated Views will pass on top of everything else.
activity_main.xml
<LinearLayout
android:id="#+id/frame"
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:clipChildren="false"
android:orientation="horizontal"
tools:context=".MainActivity">
<LinearLayout
android:id="#+id/layout1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFAABBCC"
android:orientation="vertical">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:text="I'm some text"/>
</LinearLayout>
<LinearLayout
android:id="#+id/layout2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFBBCCAA"
android:orientation="vertical">
</LinearLayout>
<LinearLayout
android:id="#+id/layout3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFCCAABB"
android:orientation="vertical">
</LinearLayout>
<LinearLayout
android:id="#+id/layout4"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFBBAACC"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
MainActivity.java
private LinearLayout mLayout1;
private LinearLayout mLayout2;
private LinearLayout mLayout3;
private LinearLayout mLayout4;
private TextView mTextView;
private View.OnTouchListener mOnTouchListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLayout1 = (LinearLayout) findViewById(R.id.layout1);
mLayout2 = (LinearLayout) findViewById(R.id.layout2);
mLayout3 = (LinearLayout) findViewById(R.id.layout3);
mLayout4 = (LinearLayout) findViewById(R.id.layout4);
mTextView = (TextView) findViewById(R.id.textView);
mOnTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// simple trigger to start the animation.
startAnimation();
mTextView.setOnTouchListener(null);
return true;
}
};
mTextView.setOnTouchListener(mOnTouchListener);
}
private void startAnimation() {
final LinearLayout origin = (LinearLayout) mTextView.getParent();
LinearLayout destination = null;
// I'm not sure what kind of behaviour you want. This just randomises the destination.
do {
switch (new Random().nextInt(4)) {
case 0:
destination = mLayout1;
break;
case 1:
destination = mLayout2;
break;
case 2:
destination = mLayout3;
break;
case 3:
destination = mLayout4;
break;
default:
}
// if destination == origin or is null, try again.
} while (destination == origin || destination == null);
// Create another TextView and initialise it to match mTextView
final TextView textViewNew = new TextView(this);
textViewNew.setText(mTextView.getText());
textViewNew.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextView.getTextSize());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
textViewNew.setLayoutParams(params);
textViewNew.setOnTouchListener(mOnTouchListener);
// Add the new TextView to the destination LinearLayout
destination.addView(textViewNew);
// Create animations based on origin and destination LinearLayouts
ObjectAnimator outAnimator = getOutAnimator(origin, destination);
// The in animator also requires a reference to the new TextView
ObjectAnimator inAnimator = getInAnimator(textViewNew, origin, destination);
// All animators must be created before any are started because they are calculated
// using values that are modified by the animation itself.
outAnimator.start();
inAnimator.start();
// Add a listener to update mTextView reference to the new TextView when complete.
inAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
origin.removeView(mTextView);
mTextView = textViewNew;
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
}
/**
* This method creates an ObjectAnimator to move the existing TextView out of its parent
* towards its destination
*/
private ObjectAnimator getOutAnimator(View origin, View destination) {
// Calculate the difference between x of destination and of origin
float layoutDifferenceX = destination.getX() - origin.getX();
// initialX is simply mTextView.getX()
// the distance moved == layoutDifferenceX
float finalX = mTextView.getX() + layoutDifferenceX;
ObjectAnimator animator = ObjectAnimator.ofFloat(mTextView, "x",
mTextView.getX(), finalX);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setDuration(500);
return animator;
}
/**
* This method creates an ObjectAnimator to move the new TextView from the initial position
* of mTextView, relative to the new TextView's parent, to its destination.
*/
private ObjectAnimator getInAnimator(View newView, View origin, View destination) {
// Calculate the difference between x of destination and of origin
float layoutDifferenceX = destination.getX() - origin.getX();
// initialX relative to destination
float initialX = mTextView.getX() - layoutDifferenceX;
// finalX relative to destination == initialX relative to origin
float finalX = mTextView.getX();
ObjectAnimator animator = ObjectAnimator.ofFloat(newView, "x",
initialX, finalX);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setDuration(500);
return animator;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
EDIT: You could also declare the TextView in xml and inflate it to get rid of all the code initialising it.
I am new in Android animation and my requirement is to translate a view from one layout to layout in a single xml file on click of that view.
Scenario:
Suppose I click a button, present on the top of the header in a xml file,and it should move/translate downwards (it should give an impact that it lies on the other layout downwards to header), and also I want that when the user clicks on the same again, it should now move to its original position.
Here I am explaining with my xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/app_bg"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/top"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/header"
android:paddingLeft="10dp"
android:paddingRight="10dp" >
<Button
android:id="#+id/btnSearchHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerInParent="true"
android:background="#drawable/search_icon" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/bottom"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/app_transparent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_marginTop="10dp"
android:visibility="visible" >
<Button
android:id="#+id/btnMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dp"
android:text="ABC" />
<Button
android:id="#+id/btnSearchSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/btnMenu"
android:text="CDE" />
</RelativeLayout>
</LinearLayout>
MORE PRECISE REQUIREMENT SPECIFICATION (Kindly read carefully:)
Here I have two sub inner layouts:-
Top Layout - id-> top
Bottom Layout- id -> bottom
Now a view (Button -> btnSearchHeader) is lying in my top layout and I want to animate the same to the bottom layout (it should give an impact that it is translated with a translate animation to the bottom layout) on click of that button and when the user clicks on that button, it should again translate back to its original position with a translate animation .. i.e it should show back in the top layout
I have no idea how to give these impacts using translate animations, however i just have a basic translate animation knowledge which is insufficient for me to work upon my requirement.
Any type of related help is appreciable.
Thanks
Have you tried something simple like the following?
final int topHeight = findViewById(R.id.top).getHeight();
final int bottomHeight = findViewById(R.id.bottom).getHeight();
final View button = findViewById(R.id.btnSearchHeader);
final ObjectAnimator moveDownAnim = ObjectAnimator.ofFloat(button, "translationY", 0.F, topHeight + bottomHeight / 2 - button.getHeight() / 2);
final ObjectAnimator moveUpAnim = ObjectAnimator.ofFloat(button, "translationY", topHeight + bottomHeight / 2 - button.getHeight() / 2, 0.F);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (0.F == v.getTranslationY())
moveDownAnim.start();
else
moveUpAnim.start();
}
});
If you actually need the button view to change parents, you can use AnimatorListener to achieve this at the end of each animation. Something like:
moveDownAnim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animation) {
((ViewGroup)findViewById(R.id.top)).removeView(button);
((ViewGroup)findViewById(R.id.bottom)).addView(button);
((RelativeLayout)button.getLayoutParams()).addRule(RelativeLayout.CENTER_IN_PARENT);
button.setTranslationY(0.F); // Since it is now positioned in the new layout, no need for translation.
}
#Override
public void onAnimationCancel(Animator animation) { /* NOP */ }
#Override
public void onAnimationRepeat(Animator animation) { /* NOP */ }
#Override
public void onAnimationStart(Animator animation) { /* NOP */ }
});
(And analogous listener for the moveUpAnim.)
However, I doubt you need to actually do this to achieve the visual effect you want. But if you do this part, you will probably also need to set a fixed height for your top view as opposed to wrap_content. (Otherwise, if a layout pass happens while the button has been moved to the bottom view, the top layout's height might go to 0 if there's nothing else in it.) Easiest would be to just do this directly in the xml layout file. However, if you want to "do it on the fly", you can change the layout's height in the onAnimationEnd() method using something like:
#Override
public void onAnimationEnd(Animator animation) {
final ViewGroup topLayout = findViewById(R.id.top);
topLayout.getLayoutParams().height = topLayout.getHeight(); // Keep it the same height...
topLayout.removeView(button);
((ViewGroup)findViewById(R.id.bottom)).addView(button);
((RelativeLayout)button.getLayoutParams()).addRule(RelativeLayout.CENTER_IN_PARENT);
button.setTranslationY(0.F); // Since it is now positioned in the new layout, no need for translation.
}
I encountered a problem when embedding a ListView inside a ScrollView, or at least that's where I guess the problem comes from. The ListView element is a fairly simple one:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/item_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/general_background_list_middle"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:paddingRight="0dp"
android:paddingLeft="0dp">
<ImageView
android:id="#+id/chat_friends_avatar"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginLeft="7dp"
android:layout_marginRight="8dp"
android:paddingRight="0dp"
android:paddingLeft="0dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="#drawable/friends_icon_avatar_default"/>
<TextView
android:id="#+id/chat_message_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/chat_friends_avatar"
android:layout_alignParentTop="true"
android:layout_marginRight="35dp"
android:maxLines="10"
android:textSize="12dp"/>
<TextView
android:id="#+id/chat_friend_name"
android:layout_width="140dp"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
style="#style/SubText"
android:layout_toRightOf="#id/chat_friends_avatar"
android:layout_below="#id/chat_message_text" />
<TextView
android:id="#+id/chat_message_time"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginTop="3dp"
style="#style/SubText"
android:layout_alignParentRight="true"
android:layout_below="#id/chat_message_text" />
</RelativeLayout>
However, when I embed a list of such elements in a ScrollView, in between some other elements, the rows are not fully displayed, they are clipped (see image below) if the text is wrapped. The ListView is instantiated as follows in the ScrollView:
<ListView
android:id="#+id/info_chat_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:cacheColorHint="#color/frame_background_color"
android:clickable="false"
android:divider="#null"
android:footerDividersEnabled="false"
android:focusable="false" >
</ListView>
If the height of the ListView is set to "wrap_content" only the first element is shown. That's why I'm using a method to calculate the height of the rows of the list:
private int getCommentsListHeight() {
if (mChatAdapter != null && mChatAdapter.getCount() != 0) {
if (mChatList != null) {// && mCommentsListItemHeight == 0) {
mCommentsListItemHeight = 0;
for (int i = 0; i < mChatAdapter.getCount(); i++) {
// Get view item height
View viewItem = mChatAdapter
.getView(i, new View(OnAirActivity.this), mChatList);
viewItem.measure(0, 0);
Logger.d(LOGTAG, "View " + i + " measured height = " + viewItem.getMeasuredHeight());
mCommentsListItemHeight += viewItem.getMeasuredHeight();
}
}
//return mChatAdapter.getCount() * mCommentsListItemHeight;
return mCommentsListItemHeight;
} else {
return 0;
}
}
Unfortunately, in case when the text inside the TextView is wrapped, even over several lines, the height of the row element returned by the getMeasuredHeight() method is constant. Also the getLineCount() called on the TextView inside the row element returns 1 even if the text is wrapped.
On the other hand, if this ListView is embedded in a LinearLayout, everything works fine and the full list is displayed with no clipping.
Do you have any suggestions as to what might be wrong here? I really don't like the idea of manually measuring the height of the list elements and it apparently doesn't work but why can't android nicely stretch the ListView inside the ScrollView to fit it all in there?
Clipped list:
Use this method created by https://stackoverflow.com/users/205192/dougw
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
It's a BAD practice to encapsulate ListView within a ScrollView because ListView itself contains scrolling capabilities. You should implement a solution that does not contain such hierarchy of views and I hope it will do the magic :)
Here resource of main layout with ScrollView:
<ScrollView android:layout_height="fill_parent" android:layout_width="fill_parent">
<LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="#+id/parentLayout"/>
</ScrollView>
Here the code to insert items:
parentLayout.removeAllViews();
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (int i = comments.size() - 1; i >= 0; i--) {
CommentInfo comment = comments.get(i);
View view = inflater.inflate(your_resource_id, null, false);
TextView commentsContent =(TextView)view.findViewById(R.id.commentContent);
if (commentsContent != null) {
String data = String.format("%s (by %s, %s)", comment.getCommentText(), comment.getUserName(),
commentsContent.setTextSize(st.getTextSize());
commentsContent.setText(data);
}
parentLayout.addView(view, 0);
}
I had the same problem in my project.You need to create simple LinearLayout inside ScrollView. After that you need create new View with your listview item xml using LayoutInflater. After creation put all data in new View and add to LinearLayout as child view:
linearLayot.addView(newView, position_you_need).
Hope it would help you!
I took the recommendation of not using a ListView element inside a ScrollView to heart and decided to use a slightly brute force method to achieve what I need. Since there is a constant number of up to five list rows that need to be displayed I removed the ListView instantiation from the xml file and replaced it with five instances of rows:
<include android:id="#+id/info_comment_1" layout="#layout/chat_single_message" />
<include android:id="#+id/info_comment_2" layout="#layout/chat_single_message" />
<include android:id="#+id/info_comment_3" layout="#layout/chat_single_message" />
<include android:id="#+id/info_comment_4" layout="#layout/chat_single_message" />
<include android:id="#+id/info_comment_5" layout="#layout/chat_single_message" />
In the Activity class I declare five placeholders for these views:
private RelativeLayout mChatMessages[] = new RelativeLayout[COMMENTS_NUMBER];
and initialize them with:
mChatMessages[0] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_1);
mChatMessages[1] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_2);
mChatMessages[2] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_3);
mChatMessages[3] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_4);
mChatMessages[4] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_5);
Then, whenever a new message is received I use the ChatAdapter (the same I used for the ListView previously) and call its getView() method:
protected void updateChatMessages() {
int msgCount = mChatAdapter.getCount();
for (int i = 0; i < COMMENTS_NUMBER; i++) {
if (msgCount <= i) {
mChatMessages[i].setVisibility(View.GONE);
} else {
mChatMessages[i] = (RelativeLayout) mChatAdapter.getView(i, mChatMessages[i], null);
mChatMessages[i].setVisibility(View.VISIBLE);
}
}
}
I don't inflate the pariculat views ever again since the only thing that changes is the content of each row, not the layout. This means there is no performance penalty here.
This is basically a manual implementation of a ListView with a limited maximum number of elements. This time, however, ScrollView is able to fit them nicely and nothing gets clipped.
For a dynamic number of rows the approach suggested by Layko could be employed with the views being instantiated programatically and added to the LinearLayout inside the ScrollView.
I can see the ListView is inside a ViewPager; one other simple approach to resolving this issue is to add
app:layout_behavior="#string/appbar_scrolling_view_behavior" to the ViewPager in your layout xml as seen below.
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
To prevent the same behavior at the bottom of the list, you can also add android:layout_marginBottom="?attr/actionBarSize" to the ViewPager like so
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_marginBottom="?attr/actionBarSize"/>
This is coming late, but I hope it helps any other person.
try it..
after create all view add bellow line for ScrollView location on screen (x,y)
ScrollView scrollView = (ScrollView)findViewById(R.id.scrollView);
scrollView.smoothScrollTo(0,0);// top location zero index
I had a similar problem. I have a
RelativeLayout
listView
includeLayout
where I include some bottom nav beneath the listView with this
<include
android:id="#+id/includeLayout"
layout="#layout/bottom_nav_bar"
and my listView was clipped - not taking the full height available between the header and bottom nav. I tried various xml settings suggested in this and other threads, but what worked for me was to add
android:layout_alignBottom="#+id/includeLayout"
to my listView. That seemed to pull the listView down to the top of the bottom nav, so that the listView is now using the full available height (and it scrolls as needed).
This works for me
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/activity_vertical_margin">
<TextView
android:id="#+id/tv_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="17sp"
android:text="#string/text_list_devices" />
<ListView
android:id="#+id/lv_paired"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:cacheColorHint="#00000000"
android:layout_above="#+id/signup_t"
android:layout_below="#id/tv_status"
/>
<Button
android:id="#+id/signup_t"
style="?android:textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textColor="#android:color/white"
android:layout_alignParentBottom="true"
android:text="Print All Records"
android:typeface="sans"
android:layout_marginLeft="45dp"
android:layout_marginRight="45dp"
android:textSize="24sp"
android:background="#drawable/selector_for_button"
android:textStyle="bold" />
</RelativeLayout>
This is BAD practice. But there are some situations we can not avoid using that. For example dynamic e-commerce layouts we may put multiple lists or recycle views but you don't want to scroll inside a single item height (if accidentally wanted!!). I faced this kind of problem. I fixed using a simple way. I don't tell this is the correct way but it may help some.
!! I used to recycle the view.
(01) Create an Interface to return view height.
public interface AfterViewLoadListener {
void onViewHeightMeasured(int height, String mode);
}
(02) implement with your activity
public class *Activity extends AppCompatActivity implements AfterViewLoadListener{
/** your codes **/
final SimpleListRecycleAdapter order_adapter = new SimpleListRecycleAdapter(this,"ORDER");
}
#Override
public void onViewHeightMeasured(int height, String mode) {
if(mode.equals("ORDER") && height > 0){
recycleView.setMinimumHeight(height);
}
}
(03) inside the recycle view custom adapter
AfterViewLoadListener viewLoadListener = null;
public SimpleListRecycleAdapter(AfterViewLoadListener listener, String mode) {
if(listener instanceof AfterViewLoadListener){
viewLoadListener = listener;
}
this.mode = mode;
}
(04) override the onViewAttachedToWindow method
#Override
public void onViewAttachedToWindow(#NonNull SimpleListViewHolder holder) {
super.onViewAttachedToWindow(holder);
View view = holder.itemView;
view.measure(0, 0);
this.viewMinHeight = view.getMeasuredHeight();
if(!firstFlag){
firstFlag = true;
viewLoadListener.onViewHeightMeasured(this.viewMinHeight*filtered.length(),mode);
}
}
(05) That's it. It worked for me.