How to change shape by controlling it from points and line - android

I have a trapezoid shape created in custom view using below code.
#Override
protected void onDraw(Canvas canvas) {
trapezoidPath.moveTo(0,0);
trapezoidPath.lineTo(getWidth() ,0);
trapezoidPath.lineTo(getWidth() , altitude);
trapezoidPath.lineTo(0,getHeight());
trapezoidPath.lineTo(0,0);
trapezoidPath.close();
canvas.drawPath(trapezoidPath,paintTrapezoid);
}
The drawn shape looks like this.
I want to move (0,height) point to top until trapezoid shape become a rectangle. After that I want to move bottom line up until shape become a line.
Is there any way to access created path lines and it's point and manipulate them to achieve what I want ? If not how can I achieve this ?
I have to animate this shape base on user response. Thank you.

Use an ObjectAnimator to change the variables you use in onDraw. Below is an example of how you could implement this.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TrapezoidView trapezoidView = findViewById(R.id.trapezoid);
final Button resetButton = findViewById(R.id.btn_reset);
resetButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
trapezoidView.reset();
}
});
final Button animateButton = findViewById(R.id.btn_animate);
animateButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
animateButton.setEnabled(false);
resetButton.setEnabled(false);
trapezoidView.toNewState();
}
});
trapezoidView.setListener(new TrapezoidView.TrapezoidListener() {
#Override
public void onNewState() {
animateButton.setEnabled(true);
resetButton.setEnabled(true);
}
});
}
}
TrapezoidView.java
public class TrapezoidView extends View {
public interface TrapezoidListener {
void onNewState();
}
public static final int TRAPEZOID_STATE = 0;
public static final int RECTANGLE_STATE = 1;
public static final int LINE_STATE = 2;
private int mState = TRAPEZOID_STATE;
private Paint mTrapezoidPaint;
private Path mTrapezoidPath = new Path();
private int mAnimationDuration = 5000; // 5 s in millis
private float mRectangleHeight = dpTopx(200);
private float mAltitude = mRectangleHeight;
private TrapezoidListener mListener;
public TrapezoidView(Context context, AttributeSet attrs) {
super(context, attrs);
mTrapezoidPaint = new Paint();
mTrapezoidPaint.setColor(Color.BLACK);
mTrapezoidPaint.setStrokeWidth(5.0f);
mTrapezoidPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mTrapezoidPath.reset();
if (mState == TRAPEZOID_STATE) {
mAltitude = getHeight();
}
mTrapezoidPath.moveTo(0, 0);
mTrapezoidPath.lineTo(getWidth(), 0);
if (mState == LINE_STATE) {
mTrapezoidPath.lineTo(getWidth(), mAltitude);
mTrapezoidPath.lineTo(0, mAltitude);
} else {
mTrapezoidPath.lineTo(getWidth(), mRectangleHeight);
mTrapezoidPath.lineTo(0, mAltitude);
}
mTrapezoidPath.lineTo(0, 0);
mTrapezoidPath.close();
canvas.drawPath(mTrapezoidPath, mTrapezoidPaint);
}
private float dpTopx(int dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
public float getAltitude() {
return mAltitude;
}
public void setAltitude(float altitude) {
this.mAltitude = altitude;
invalidate();
}
public void reset() {
mState = TRAPEZOID_STATE;
mRectangleHeight = dpTopx(200);
mAltitude = mRectangleHeight;
invalidate();
}
public void setListener(TrapezoidListener listener) {
mListener = listener;
}
public void toNewState() {
if (mState == LINE_STATE) {
mListener.onNewState();
return;
}
float start;
float target;
final int targetState = mState == TRAPEZOID_STATE ? RECTANGLE_STATE : LINE_STATE;
if (targetState == RECTANGLE_STATE) {
start = getHeight();
target = mRectangleHeight;
} else {
start = mAltitude;
target = 0.0f;
}
ObjectAnimator stateAnimation = ObjectAnimator.ofFloat(TrapezoidView.this, "Altitude", start);
stateAnimation.setFloatValues(target);
stateAnimation.setDuration(mAnimationDuration);
stateAnimation.addListener(
new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
mState = targetState;
}
#Override
public void onAnimationEnd(Animator animation) {
mListener.onNewState();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
}
);
stateAnimation.start();
}
}
activity_main.xml
<LinearLayout 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:orientation="vertical"
android:padding="50dp"
tools:context="test.example.MainActivity">
<test.example.TrapezoidView
android:id="#+id/trapezoid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.9" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.1"
android:orientation="horizontal">
<Button
android:id="#+id/btn_animate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Animate!" />
<Button
android:id="#+id/btn_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reset" />
</LinearLayout>
</LinearLayout>

Related

Custom Drawing View load drawable image inside it

I've searched, but haven't found anything I'm looking for, perhaps. I want load image into my custom drawing view. Here is the code of DrawingView that extend View.
private Path mDrawPath;
private Paint mBackgroundPaint;
private Paint mDrawPaint;
private Canvas mDrawCanvas;
private Bitmap mCanvasBitmap;
private ArrayList<Path> mPaths = new ArrayList<>();
private ArrayList<Paint> mPaints = new ArrayList<>();
private ArrayList<Path> mUndonePaths = new ArrayList<>();
private ArrayList<Paint> mUndonePaints = new ArrayList<>();
// Set default values
private int mBackgroundColor = 0xFFFFFFFF;
private int mPaintColor = 0xFF660000;
private int mStrokeWidth = 10;
public DrawingView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
private void init()
{
mDrawPath = new Path();
mBackgroundPaint = new Paint();
initPaint();
}
private void initPaint()
{
mDrawPaint = new Paint();
mDrawPaint.setColor(mPaintColor);
mDrawPaint.setAntiAlias(true);
mDrawPaint.setStrokeWidth(mStrokeWidth);
mDrawPaint.setStyle(Paint.Style.STROKE);
mDrawPaint.setStrokeJoin(Paint.Join.ROUND);
mDrawPaint.setStrokeCap(Paint.Cap.ROUND);
}
private void drawBackground(Canvas canvas)
{
mBackgroundPaint.setColor(Color.TRANSPARENT);
mBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
mBackgroundPaint.setAntiAlias(true);
canvas.drawRect(0, 0, this.getWidth(), this.getHeight(), mBackgroundPaint);
}
private void drawPaths(Canvas canvas)
{
int i = 0;
for (Path p : mPaths)
{
canvas.drawPath(p, mPaints.get(i));
i++;
}
}
#Override
protected void onDraw(Canvas canvas)
{
drawBackground(canvas);
drawPaths(canvas);
canvas.drawPath(mDrawPath, mDrawPaint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
mCanvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mDrawCanvas = new Canvas(mCanvasBitmap);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
mDrawPath.moveTo(touchX, touchY);
//mDrawPath.addCircle(touchX, touchY, mStrokeWidth/10, Path.Direction.CW);
break;
case MotionEvent.ACTION_MOVE:
mDrawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
mDrawPath.lineTo(touchX, touchY);
mPaths.add(mDrawPath);
mPaints.add(mDrawPaint);
mDrawPath = new Path();
initPaint();
break;
default:
return false;
}
invalidate();
return true;
}
public void clearCanvas()
{
mPaths.clear();
mPaints.clear();
mUndonePaths.clear();
mUndonePaints.clear();
mDrawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
invalidate();
}
public void setPaintColor(int color)
{
mPaintColor = color;
mDrawPaint.setColor(mPaintColor);
}
public void setPaintStrokeWidth(int strokeWidth)
{
mStrokeWidth = strokeWidth;
mDrawPaint.setStrokeWidth(mStrokeWidth);
}
public void setBackgroundColor(int color)
{
mBackgroundColor = color;
mBackgroundPaint.setColor(mBackgroundColor);
invalidate();
}
public Bitmap getBitmap()
{
drawBackground(mDrawCanvas);
drawPaths(mDrawCanvas);
return mCanvasBitmap;
}
public void undo()
{
if (mPaths.size() > 0)
{
mUndonePaths.add(mPaths.remove(mPaths.size() - 1));
mUndonePaints.add(mPaints.remove(mPaints.size() - 1));
invalidate();
}
}
public void redo()
{
if (mUndonePaths.size() > 0)
{
mPaths.add(mUndonePaths.remove(mUndonePaths.size() - 1));
mPaints.add(mUndonePaints.remove(mUndonePaints.size() - 1));
invalidate();
}
}
BoardActivity class code is:
private TextView titolo;
private DrawingView areaDisegno;
private ImageView sceltaColore, sceltaSpessore, undo, redo;
private Bitmap drawable;
private int mCurrentBackgroundColor;
private int mCurrentColor;
private int mCurrentStroke;
private static final int MAX_STROKE_WIDTH = 50;
#Override
protected void onCreate(Bundle savedInstanceState) {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setNavigationBarColor(getResources().getColor(R.color.black));
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_board1);
titolo = findViewById(R.id.titolo);
titolo.setText("MATCHUP BOARD 1");
areaDisegno = findViewById(R.id.area_disegno);
sceltaColore= findViewById(R.id.scelta_colore);
sceltaSpessore = findViewById(R.id.scelta_spessore);
undo = findViewById(R.id.undo);
redo = findViewById(R.id.redo);
sceltaColore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startColorPickerDialog();
}
});
sceltaSpessore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startStrokeSelectorDialog();
}
});
undo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
areaDisegno.undo();
}
});
redo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
areaDisegno.redo();;
}
});
inizializzaAreaDisegno();
}
private void inizializzaAreaDisegno()
{
// mCurrentBackgroundColor = ContextCompat.getColor(this, android.R.color.white);
mCurrentColor = ContextCompat.getColor(this, android.R.color.black);
mCurrentStroke = 10;
areaDisegno.setBackgroundColor(mCurrentBackgroundColor);
areaDisegno.setPaintColor(mCurrentColor);
areaDisegno.setPaintStrokeWidth(mCurrentStroke);
}
private void startFillBackgroundDialog()
{
int[] colors = getResources().getIntArray(R.array.palette);
ColorPickerDialog dialog = ColorPickerDialog.newInstance(R.string.color_picker_default_title,
colors,
mCurrentBackgroundColor,
5,
ColorPickerDialog.SIZE_SMALL);
dialog.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
{
#Override
public void onColorSelected(int color)
{
mCurrentBackgroundColor = color;
areaDisegno.setBackgroundColor(mCurrentBackgroundColor);
}
});
dialog.show(getFragmentManager(), "ColorPickerDialog");
}
private void startColorPickerDialog()
{
int[] colors = getResources().getIntArray(R.array.palette);
ColorPickerDialog dialog = ColorPickerDialog.newInstance(R.string.color_picker_default_title,
colors,
mCurrentColor,
5,
ColorPickerDialog.SIZE_SMALL);
dialog.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
{
#Override
public void onColorSelected(int color)
{
mCurrentColor = color;
areaDisegno.setPaintColor(mCurrentColor);
}
});
dialog.show(getFragmentManager(), "ColorPickerDialog");
}
private void startStrokeSelectorDialog()
{
StrokeSelectorDialog dialog = StrokeSelectorDialog.newInstance(mCurrentStroke, MAX_STROKE_WIDTH);
dialog.setOnStrokeSelectedListener(new StrokeSelectorDialog.OnStrokeSelectedListener()
{
#Override
public void onStrokeSelected(int stroke)
{
mCurrentStroke = stroke;
areaDisegno.setPaintStrokeWidth(mCurrentStroke);
}
});
dialog.show(getSupportFragmentManager(), "StrokeSelectorDialog");
}
private void startShareDialog(Uri uri)
{
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "");
intent.putExtra(android.content.Intent.EXTRA_TEXT, "");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "Share Image"));
}
private void requestPermissionsAndSaveBitmap()
{
if (PermissionManager.checkWriteStoragePermissions(this))
{
Uri uri = FileManager.saveBitmap(this, areaDisegno.getBitmap());
startShareDialog(uri);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode)
{
case PermissionManager.REQUEST_WRITE_STORAGE:
{
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
Uri uri = FileManager.saveBitmap(this, areaDisegno.getBitmap());
startShareDialog(uri);
} else
{
Toast.makeText(this, "The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show();
}
}
}
}
And Layout used is:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
android:background="#drawable/sfondo"
tools:context=".activities.Board1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar_menu_disegno"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/toolbar"
android:orientation="vertical"
android:weightSum="2"
>
<FrameLayout
android:layout_weight="1.99"
android:layout_width="match_parent"
android:layout_height="0dp" >
<it.colan.matchup.ui.component.DrawingView
android:id="#+id/area_disegno"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:rotation="90"
android:src="#drawable/board_1" />
</FrameLayout>
<LinearLayout
android:id="#+id/menu_strumenti"
android:layout_weight="0.01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/area_disegno"
android:orientation="horizontal"
android:gravity="center"
android:background="#color/black">
<ImageView
android:id="#+id/scelta_colore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_palette"
android:padding="15dp"
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"/>
<ImageView
android:id="#+id/scelta_spessore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_peso_linea"
android:padding="15dp"
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"/>
<ImageView
android:id="#+id/undo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_undo"
android:padding="15dp"
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"/>
<ImageView
android:id="#+id/redo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_redo"
android:padding="15dp"
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
I have two problems:
The drawing does it for me behind the image and not above
imagebug
The background of the image is not full screen with margins.
How should I do?
Thanks
Cris

How to programmatically reMeasure view?

I have CustomView with 2 RelativeLayouts. There are RecyclerViews in each RelativeLayout. When I add new element to RecyclerView it doesn`t change height.
If I change screen orientation then android measures it well. So my question is how to programmatically tell android that he needs remeasure both childs and parent elements.
requestlayout() and invalidate() doesn`t work
CustomView:
public class ExpandableView extends LinearLayout {
private Settings mSettings ;
private int mExpandState;
private ValueAnimator mExpandAnimator;
private ValueAnimator mParentAnimator;
private AnimatorSet mExpandScrollAnimatorSet;
private int mExpandedViewHeight;
private boolean mIsInit = true;
private int defaultHeight;
private boolean isAllowedExpand = false;
private ScrolledParent mScrolledParent;
private OnExpandListener mOnExpandListener;
public ExpandableView(Context context) {
super(context);
init(null);
}
public ExpandableView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ExpandableView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init(AttributeSet attrs) {
Log.w("tag", "init");
setOrientation(VERTICAL);
this.setClipChildren(false);
this.setClipToPadding(false);
mExpandState = ExpandState.PRE_INIT;
mSettings = new Settings();
if(attrs!=null) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ExpandableView);
mSettings.expandDuration = typedArray.getInt(R.styleable.ExpandableView_expDuration, Settings.EXPAND_DURATION);
mSettings.expandWithParentScroll = typedArray.getBoolean(R.styleable.ExpandableView_expWithParentScroll,false);
mSettings.expandScrollTogether = typedArray.getBoolean(R.styleable.ExpandableView_expExpandScrollTogether,true);
typedArray.recycle();
}
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.w("tag", "onMeasure");
Log.w("tag", "widthMeasureSpec - " + widthMeasureSpec);
Log.w("tag", "heightMeasureSpec - " + heightMeasureSpec);
int childCount = getChildCount();
if(childCount!=2) {
throw new IllegalStateException("ExpandableLayout must has two child view !");
}
if(mIsInit) {
((MarginLayoutParams)getChildAt(0).getLayoutParams()).bottomMargin=0;
MarginLayoutParams marginLayoutParams = ((MarginLayoutParams)getChildAt(1).getLayoutParams());
marginLayoutParams.bottomMargin=0;
marginLayoutParams.topMargin=0;
marginLayoutParams.height = 0;
mExpandedViewHeight = getChildAt(1).getMeasuredHeight();
defaultHeight = mExpandedViewHeight;
mIsInit =false;
mExpandState = ExpandState.CLOSED;
View view = getChildAt(0);
if (view != null){
view.setOnClickListener(v -> toggle());
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.w("tag", "onSizeChanged");
if(mSettings.expandWithParentScroll) {
mScrolledParent = Utils.getScrolledParent(this);
}
}
private int getParentScrollDistance () {
int distance = 0;
Log.w("tag", "getParentScrollDistance");
if(mScrolledParent == null) {
return distance;
}
distance = (int) (getY() + getMeasuredHeight() + mExpandedViewHeight - mScrolledParent.scrolledView.getMeasuredHeight());
for(int index = 0; index < mScrolledParent.childBetweenParentCount; index++) {
ViewGroup parent = (ViewGroup) getParent();
distance+=parent.getY();
}
return distance;
}
private void verticalAnimate(final int startHeight, final int endHeight ) {
int distance = getParentScrollDistance();
final View target = getChildAt(1);
mExpandAnimator = ValueAnimator.ofInt(startHeight,endHeight);
mExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
target.getLayoutParams().height = (int) animation.getAnimatedValue();
target.requestLayout();
}
});
mExpandAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if(endHeight-startHeight < 0) {
mExpandState = ExpandState.CLOSED;
if (mOnExpandListener != null) {
mOnExpandListener.onExpand(false);
}
} else {
mExpandState=ExpandState.EXPANDED;
if(mOnExpandListener != null) {
mOnExpandListener.onExpand(true);
}
}
}
});
mExpandState=mExpandState==ExpandState.EXPANDED?ExpandState.CLOSING :ExpandState.EXPANDING;
mExpandAnimator.setDuration(mSettings.expandDuration);
if(mExpandState == ExpandState.EXPANDING && mSettings.expandWithParentScroll && distance > 0) {
mParentAnimator = Utils.createParentAnimator(mScrolledParent.scrolledView, distance, mSettings.expandDuration);
mExpandScrollAnimatorSet = new AnimatorSet();
if(mSettings.expandScrollTogether) {
mExpandScrollAnimatorSet.playTogether(mExpandAnimator,mParentAnimator);
} else {
mExpandScrollAnimatorSet.playSequentially(mExpandAnimator,mParentAnimator);
}
mExpandScrollAnimatorSet.start();
} else {
mExpandAnimator.start();
}
}
public void setExpand(boolean expand) {
if (mExpandState == ExpandState.PRE_INIT) {return;}
getChildAt(1).getLayoutParams().height = expand ? mExpandedViewHeight : 0;
requestLayout();
mExpandState=expand?ExpandState.EXPANDED:ExpandState.CLOSED;
}
public boolean isExpanded() {
return mExpandState==ExpandState.EXPANDED;
}
public void toggle() {
if (isAllowedExpand){
if(mExpandState==ExpandState.EXPANDED) {
close();
}else if(mExpandState==ExpandState.CLOSED) {
expand();
}
}
}
public void expand() {
verticalAnimate(0,mExpandedViewHeight);
}
public void close() {
verticalAnimate(mExpandedViewHeight,0);
}
public interface OnExpandListener {
void onExpand(boolean expanded) ;
}
public void setOnExpandListener(OnExpandListener onExpandListener) {
this.mOnExpandListener = onExpandListener;
}
public void setExpandScrollTogether(boolean expandScrollTogether) {
this.mSettings.expandScrollTogether = expandScrollTogether;
}
public void setExpandWithParentScroll(boolean expandWithParentScroll) {
this.mSettings.expandWithParentScroll = expandWithParentScroll;
}
public void setExpandDuration(int expandDuration) {
this.mSettings.expandDuration = expandDuration;
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.w("tag", "onDetachedFromWindow");
if(mExpandAnimator!=null&&mExpandAnimator.isRunning()) {
mExpandAnimator.cancel();
mExpandAnimator.removeAllUpdateListeners();
}
if(mParentAnimator!=null&&mParentAnimator.isRunning()) {
mParentAnimator.cancel();
mParentAnimator.removeAllUpdateListeners();
}
if(mExpandScrollAnimatorSet!=null) {
mExpandScrollAnimatorSet.cancel();
}
}
public void setAllowedExpand(boolean allowedExpand) {
isAllowedExpand = allowedExpand;
}
public void increaseDistance(int size){
if(mExpandState==ExpandState.EXPANDED) {
close();
}
mExpandedViewHeight = defaultHeight + size;
}
//func just for loggs
public void showParams(){
RelativeLayout relativeLayout = (RelativeLayout) getChildAt(1);
/*relativeLayout.requestLayout();
relativeLayout.invalidate();*/
RecyclerView recyclerView = (RecyclerView) relativeLayout.getChildAt(1);
Log.d("tag", "height - " + relativeLayout.getHeight());
Log.d("tag", "childs - " + relativeLayout.getChildCount());
Log.d("tag", "recycler height - " + recyclerView.getHeight());
recyclerView.requestLayout();
recyclerView.invalidateItemDecorations();
recyclerView.invalidate();
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
layoutManager.getHeight();
Log.d("tag", " layoutManager.getHeight() - " + layoutManager.getHeight());
layoutManager.requestLayout();
layoutManager.generateDefaultLayoutParams();
layoutManager.onItemsChanged(recyclerView);
Log.d("tag", " layoutManager.getHeight()2- " + layoutManager.getHeight());
layoutManager.getChildCount();
recyclerView.getChildCount();
Log.d("tag", "manager childs - " + layoutManager.getChildCount());
Log.d("tag", "recycler childs - " + recyclerView.getChildCount());
}
}
Layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#color/grey_light_color"
>
<com.example.develop.project.Utils.ExpandableView.ExpandableView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/test_custom_view"
app:expWithParentScroll="true"
android:layout_gravity="center"
android:background="#color/grey_color"
>
<android.support.v7.widget.CardView
android:id="#+id/start_card"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="#color/white_color"
android:layout_marginTop="5dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/stage_tv4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/grey_deep_color"
android:text="Запуск"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:textSize="18sp"
/>
</RelativeLayout>
</android.support.v7.widget.CardView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/start_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/start_accept"
android:textSize="18sp"
android:layout_marginStart="15dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp"
android:textColor="#color/grey_deep_color"
android:layout_marginEnd="15dp"
/>
<android.support.v7.widget.RecyclerView
android:id="#+id/my_test_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/start_tv"
android:layout_marginTop="10dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
>
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
</com.example.develop.project.Utils.ExpandableView.ExpandableView>
<Button
android:id="#+id/add_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:text="Add elem"
android:layout_marginStart="15dp"
android:layout_marginBottom="15dp"
/>
<Button
android:id="#+id/check_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:text="Check params"
android:layout_marginBottom="15dp"
android:layout_marginEnd="15dp"
/>
</RelativeLayout>
Activity:
public class TestActivity extends MvpAppCompatActivity implements TestContract.View {
TestAdapter testAdapter;
#InjectPresenter
public TestPresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_layout);
init();
}
private void init() {
ExpandableView expandableView = findViewById(R.id.test_custom_view);
expandableView.setAllowedExpand(true);
Button add_btn = findViewById(R.id.add_btn);
Button check_btn = findViewById(R.id.check_btn);
add_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
presenter.addElem();
}
});
check_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
expandableView.showParams();
}
});
}
#Override
public void addElems(ArrayList<String> list) {
testAdapter.notifyDataSetChanged();
}
#Override
public void populateAdapter(ArrayList<String> list) {
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
RecyclerView recyclerView = findViewById(R.id.my_test_tv);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(false);
testAdapter = new TestAdapter(list);
recyclerView.setAdapter(testAdapter);
}
}
Adapter:
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.TasksViewHolder> {
private List<String> list;
public TestAdapter(List<String> list) {
this.list = list;
}
#NonNull
#Override
public TestAdapter.TasksViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.test_item, parent, false);
TestAdapter.TasksViewHolder vh = new TestAdapter.TasksViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(#NonNull TestAdapter.TasksViewHolder holder, int position) {
String text = list.get(position);
holder.textView.setText(text);
}
public static class TasksViewHolder extends RecyclerView.ViewHolder {
private TextView textView;
public TasksViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.my_test_tv);
}
}
#Override
public int getItemCount() {
return list.size();
}
}
The ExpandableView contains some suspicious code in its onMeasure method.
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
if(mIsInit) {
((MarginLayoutParams)getChildAt(0).getLayoutParams()).bottomMargin=0;
MarginLayoutParams marginLayoutParams = ((MarginLayoutParams)getChildAt(1).getLayoutParams());
marginLayoutParams.bottomMargin=0;
marginLayoutParams.topMargin=0;
marginLayoutParams.height = 0;
mExpandedViewHeight = getChildAt(1).getMeasuredHeight();
defaultHeight = mExpandedViewHeight;
mIsInit =false;
mExpandState = ExpandState.CLOSED;
View view = getChildAt(0);
if (view != null){
view.setOnClickListener(v -> toggle());
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
The mIsInit variable is set to true on creation and than to false when onMeasure is called for the first time. So the code in the condition runs only once.
But it stores value mExpandedViewHeight obtained as getChildAt(1).getMeasuredHeight() and that is current height of your relative layout, which contains (still empty) RecyclerView.
As far as I can see there's nothing in ExpadableView's code that would update this value, when you add an item to the Recylerview.
I am not sure, what would the correct/perfect implementation of the onMeasure method be (for your component). That would require some debugging and testing of the component, and perhaps some tweaks in other parts of the code.
If you wrote the component, you may want to invest more effort into debugging. If you didn't write the component, you should try to find other one, that is properly implemented. Custom components with custom measurements are an advanced topic.
If you really want to go with debugging and fixing your component, that first thing to do is to update the mExpandedViewHeight value on every measurement, but that might require updating other values that are derived from this value.

Android - Animating a layout by tapping another layout

I have two RelativeLayouts which contains one TextView each.
Basically the top layout serves as a "button". When clicked, the other layout will have its animation played (expanding downwards).
In this situation, to which layout should I add setLayoutAnimationListener() so that the top layout will not be able to be clicked in method onAnimationStart() and can be clicked in method onAnimationEnd()?
Here are the layouts I meant :
<RelativeLayout
android:id="#+id/activity_signup_step_one_dropdownTitleWrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/activity_signup_step_one_orangEmailWrapper"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp">
<TextView
android:id="#+id/activity_signup_step_one_dropdownTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/stepOneTitle"/>
</RelativeLayout>
<RelativeLayout
android:id="#+id/activity_signup_step_one_dropdownTextWrapper"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/activity_signup_step_one_dropdownTitleWrapper">
<TextView
android:id="#+id/activity_signup_step_one_dropdownText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/stepOneMessage"/>
</RelativeLayout>
The java class
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_signup_step_one);
...
RelativeLayout dropDownTitle = (RelativeLayout)findViewById(R.id.activity_signup_step_one_dropdownTitleWrapper);
RelativeLayout dropDownMessage = (RelativeLayout)findViewById(R.id.activity_signup_step_one_dropdownTextWrapper);
dropDownMessage.setVisibility(View.GONE);
dropDownTitle.setOnClickListener(translateHandler);
dropDownMessage.setLayoutAnimationListener(new Animation.AnimationListener() {
//RelativeLayout touchDisabler = (RelativeLayout) findViewById(R.id.activity_signup_step_one_dropdownTitleWrapper_filler);
RelativeLayout dropDownTitle = (RelativeLayout) findViewById(R.id.activity_signup_step_one_dropdownTitleWrapper);
#Override
public void onAnimationStart(Animation animation) {
//touchDisabler.setClickable(true);
Log.d("onStart", "Start");
dropDownTitle.setClickable(false);
//getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
//WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
#Override
public void onAnimationEnd(Animation animation) {
//touchDisabler.setClickable(false);
Log.d("onEnd", "End");
dropDownTitle.setClickable(true);
//getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
#Override
public void onAnimationRepeat(Animation animation) {
//touchDisabler.setClickable(true);
}
});
int height;
View.OnClickListener translateHandler = new View.OnClickListener() {
#Override
public void onClick(View v) {
RelativeLayout dropDownMessage = (RelativeLayout)findViewById(R.id.activity_signup_step_one_dropdownTextWrapper);
TextView testText = (TextView)findViewById(R.id.activity_signup_step_one_dropdownText);
if(dropDownMessage.getVisibility() == View.VISIBLE){
MyCustomAnimation a = new MyCustomAnimation(dropDownMessage, 350, MyCustomAnimation.COLLAPSE);
height = a.getHeight();
testText.setText(getResources().getString(R.string.stepOneMessage));
dropDownMessage.startAnimation(a);
}else{
MyCustomAnimation a = new MyCustomAnimation(dropDownMessage, 350, MyCustomAnimation.EXPAND);
a.setHeight(height);
testText.setText(getResources().getString(R.string.stepOneMessage));
dropDownMessage.startAnimation(a);
}
}
};
Here's the animation class which might be useful
public class MyCustomAnimation extends Animation {
public final static int COLLAPSE = 1;
public final static int EXPAND = 0;
private View mView;
private int mEndHeight;
private int mType;
private RelativeLayout.LayoutParams mLayoutParams;
public MyCustomAnimation(View view, int duration, int type) {
setDuration(duration);
mView = view;
mEndHeight = mView.getHeight();
mLayoutParams = ((RelativeLayout.LayoutParams) view.getLayoutParams());
mType = type;
if(mType == EXPAND) {
mLayoutParams.height = 0;
} else {
mLayoutParams.height = LayoutParams.WRAP_CONTENT;
}
view.setVisibility(View.VISIBLE);
}
public int getHeight(){
return mView.getHeight();
}
public void setHeight(int height){
mEndHeight = height;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
if(mType == EXPAND) {
mLayoutParams.height = (int)(mEndHeight * interpolatedTime);
} else {
mLayoutParams.height = (int) (mEndHeight * (1 - interpolatedTime));
}
mView.requestLayout();
} else {
if(mType == EXPAND) {
mLayoutParams.height = LayoutParams.WRAP_CONTENT;
mView.requestLayout();
}else{
mView.setVisibility(View.GONE);
}
}
}
}
final RelativeLayout dropDownTitle = (RelativeLayout) findViewById(R.id.activity_signup_step_one_dropdownTitleWrapper);
dropDownMessage.setLayoutAnimationListener(new Animation.AnimationListener() {
//RelativeLayout touchDisabler = (RelativeLayout) findViewById(R.id.activity_signup_step_one_dropdownTitleWrapper_filler);
#Override
public void onAnimationStart(Animation animation) {
//touchDisabler.setClickable(true);
Log.d("onStart", "Start");
dropDownTitle.setClickable(false);
dropDownMessage.setVisibility(View.GONE);
//getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
//WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
#Override
public void onAnimationEnd(Animation animation) {
//touchDisabler.setClickable(false);
Log.d("onEnd", "End");
dropDownTitle.setClickable(true);
//getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
#Override
public void onAnimationRepeat(Animation animation) {
//touchDisabler.setClickable(true);
}
});
Try to instantiate your RelativeLayout outside of your anonymous inner class (AnimationListener) - and of course declare it final.

android ValueAnimator Performance issues,get lag after some work

I was developing user "eluleci" (https://gist.github.com/eluleci/6e0d02c766b27f6a5253) code
it's rippleTuch for preLilop
ok , it's extend Button class for do it,
when we have one button in our layout, all things work good and animation work smooth,
but when we add more than one button , like 5 button, by touch them the animation on the buttons get lag and not smooth!!
I cant understand what's the problem? Memory issues? and how can I fix it?can anyone knew where is the problem?
thank you for your attention
here is my code:
public class MehDiRippleButton extends Button {
#Override
public boolean isInEditMode() {
return true;
}
Paint paint=new Paint();
private static final int AnimDuration = 9000;
private TouchEffectAnimator touchEffectAnimator;
public MehDiRippleButton(Context context) {
super(context);
init();
}
public MehDiRippleButton(Context context, AttributeSet attrs) {
super(context, attrs);
allocated_TuchEffectAnimator();
TypedArray typedArray_MehDi_rippleButton_style = context.obtainStyledAttributes(attrs,R.styleable.MehDi_rippleButton_style);
CharSequence charSequence_buttonColor =typedArray_MehDi_rippleButton_style.getString(R.styleable.MehDi_rippleButton_style_MehDi_buttonColor);
paint.setColor(Color.parseColor(charSequence_buttonColor.toString()));
touchEffectAnimator.setStroke(paint);
CharSequence charSequence_rippleColor =typedArray_MehDi_rippleButton_style.getString(R.styleable.MehDi_rippleButton_style_MehDi_rippleColor);
if(charSequence_rippleColor!=null) {
touchEffectAnimator.setEffectColor(Color.parseColor(charSequence_rippleColor.toString()),Color.parseColor(charSequence_buttonColor.toString()));
}else touchEffectAnimator.setEffectColor(Color.LTGRAY,Color.WHITE);
typedArray_MehDi_rippleButton_style.recycle();
init();
}
private void allocated_TuchEffectAnimator(){
touchEffectAnimator = new TouchEffectAnimator(this);
}
private void init() {
touchEffectAnimator.setHasRippleEffect(true);
touchEffectAnimator.setAnimDuration(AnimDuration);
touchEffectAnimator.setClipRadius(0);
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
}
});
}
#Override
public boolean onTouchEvent(final MotionEvent event) {
touchEffectAnimator.onTouchEvent(event);
return super.onTouchEvent(event);
}
#Override
protected void onDraw(Canvas canvas) {
touchEffectAnimator.onDraw(canvas);
super.onDraw(canvas);
}
}
and :
(in this class occurred the problem):
public class TouchEffectAnimator {
float u=0;
private static final int fadeout_time_helper=29;
private static final int tuchUp_time_helper=14;
private final int EASE_ANIM_DURATION = 2000;
private final int RIPPLE_ANIM_DURATION = 3000;
private final int MAX_RIPPLE_ALPHA = 255;
private View mView;
private int mClipRadius;
private boolean hasRippleEffect = false;
private int animDuration = EASE_ANIM_DURATION;
private int requiredRadius;
private float mDownX;
private float mDownY;
private float mRadius;
private int mCircleAlpha = MAX_RIPPLE_ALPHA;
private int mRectAlpha = 0;
private Paint mCirclePaint = new Paint();
private Paint mStrokePaint = new Paint();
private Paint mRectPaint = new Paint();
private Path mCirclePath = new Path();
private Path mRectPath = new Path();
private boolean isTouchReleased = false;
private boolean isAnimatingFadeIn = false;
/**
*
*
* */
private Animation.AnimationListener animationListener = new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
isAnimatingFadeIn = true;
}
#Override
public void onAnimationEnd(Animation animation) {
isAnimatingFadeIn = false;
if (isTouchReleased){
setStroke(mStrokePaint);
fadeOutEffect();
}
}
#Override
public void onAnimationRepeat(Animation animation) {
}
};
public TouchEffectAnimator(View mView) {
this.mView = mView;
}
public void setHasRippleEffect(boolean hasRippleEffect) {
this.hasRippleEffect = hasRippleEffect;
if (hasRippleEffect) animDuration = RIPPLE_ANIM_DURATION;
}
public void setAnimDuration(int animDuration) {
this.animDuration = animDuration;
}
public void setEffectColor(int effectColor,int buttonColor) {
mCirclePaint.setColor(effectColor);
mCirclePaint.setAlpha(mCircleAlpha);
mRectPaint.setColor(effectColor);
mRectPaint.setAlpha(mRectAlpha);
mStrokePaint.setColor(buttonColor);
}
public void setClipRadius(int mClipRadius) {
this.mClipRadius = mClipRadius;
}
public void onTouchEvent(final MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
isTouchReleased = true;
Drawable background = mView.getBackground();
if (background instanceof ShapeDrawable) {
((ShapeDrawable)background).getPaint().setStrokeWidth(0);
Log.e("fdsfsfd","fdsfdsfdsf");
} else if (background instanceof GradientDrawable) {
//((GradientDrawable)background).setS
Log.e("qqq","qqq");
}
ValueGeneratorAnim valueGeneratorAnim = new ValueGeneratorAnim(new InterpolatedTimeCallback() {
#Override
public void onTimeUpdate(float interpolatedTime) {
if (hasRippleEffect)
mRadius = requiredRadius * interpolatedTime +u;
mRectAlpha = (int) (interpolatedTime * MAX_RIPPLE_ALPHA);
mView.invalidate();
}
});
valueGeneratorAnim.setInterpolator(new DecelerateInterpolator());
valueGeneratorAnim.setDuration(animDuration/tuchUp_time_helper);
valueGeneratorAnim.setAnimationListener(animationListener);
mView.startAnimation(valueGeneratorAnim);
if (!isAnimatingFadeIn) {
setStroke(mStrokePaint);
fadeOutEffect();
}
} else if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
// gets the bigger value (width or height) to fit the circle
requiredRadius = mView.getWidth() >= mView.getHeight() ? mView.getWidth() : mView.getHeight();
final int requiredRadius2 = mView.getWidth() < mView.getHeight() ? mView.getWidth() : mView.getHeight();
noStroke(mStrokePaint);
requiredRadius *= 1.2;
Log.e("req ", "" + requiredRadius);
isTouchReleased = false;
mDownX = event.getX();
mDownY = mView.getMeasuredHeight()/2;
Drawable background = mView.getBackground();
if (background instanceof ShapeDrawable) {
((ShapeDrawable)background).getPaint().setStrokeWidth(0);
Log.e("fdsfsfd","fdsfdsfdsf");
} else if (background instanceof GradientDrawable) {
//((GradientDrawable)background).setS
Log.e("qqq","qqq");
}
mCircleAlpha = MAX_RIPPLE_ALPHA;
mRectAlpha = 0;
ValueGeneratorAnim valueGeneratorAnim = new ValueGeneratorAnim(new InterpolatedTimeCallback() {
#Override
public void onTimeUpdate(float interpolatedTime) {
Log.e("1 ", "" + 1);
if (hasRippleEffect)
mRadius = requiredRadius * interpolatedTime+(float)(requiredRadius2/1.5);
u=mRadius;
Log.e("mRa ", "" + mRadius);
Log.e("int ", "" + interpolatedTime);
mRectAlpha = (int) (interpolatedTime * MAX_RIPPLE_ALPHA);
mView.invalidate();
}
});
Log.e("222 ", "" + 222);
valueGeneratorAnim.setInterpolator(new DecelerateInterpolator());
valueGeneratorAnim.setDuration(animDuration);
valueGeneratorAnim.setAnimationListener(animationListener);
mView.startAnimation(valueGeneratorAnim);
}
}
public void onDraw(final Canvas canvas) {
if (hasRippleEffect) {
mCirclePath.reset();
mCirclePaint.setAlpha(mCircleAlpha);
mCirclePath.addRoundRect(new RectF(0, 0, mView.getWidth(), mView.getHeight()),
mClipRadius, mClipRadius, Path.Direction.CW);
canvas.clipPath(mCirclePath);
canvas.drawCircle(mDownX, mDownY, mRadius, mCirclePaint);
}
mRectPath.reset();
if (hasRippleEffect && mCircleAlpha != 255) mRectAlpha = mCircleAlpha / 2;
mRectPaint.setAlpha(mRectAlpha);
canvas.drawRoundRect(new RectF(0, 0, mView.getWidth(), mView.getHeight()), mClipRadius,mClipRadius, mRectPaint);
}
private void fadeOutEffect() {
ValueGeneratorAnim valueGeneratorAnim = new ValueGeneratorAnim(new InterpolatedTimeCallback() {
#Override
public void onTimeUpdate(float interpolatedTime) {
mCircleAlpha = (int) (MAX_RIPPLE_ALPHA - (MAX_RIPPLE_ALPHA * interpolatedTime));
mRectAlpha = mCircleAlpha;
mView.invalidate();
}
});
valueGeneratorAnim.setDuration(animDuration / fadeout_time_helper); /**change anim fade out time*/
mView.startAnimation(valueGeneratorAnim);
}
class ValueGeneratorAnim extends Animation {
private InterpolatedTimeCallback interpolatedTimeCallback;
ValueGeneratorAnim(InterpolatedTimeCallback interpolatedTimeCallback) {
this.interpolatedTimeCallback = interpolatedTimeCallback;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
this.interpolatedTimeCallback.onTimeUpdate(interpolatedTime);
}
}
interface InterpolatedTimeCallback {
public void onTimeUpdate(float interpolatedTime);
}
public void setStroke(Paint mStrokePaint){
GradientDrawable gradiant_withStroke = new GradientDrawable();
gradiant_withStroke.setColor(mStrokePaint.getColor());
gradiant_withStroke.setStroke(8, Color.parseColor("#00000000"));
mView.setBackgroundDrawable(gradiant_withStroke);
}
public void noStroke(Paint mStrokePaint){
GradientDrawable gradiant_withStroke = new GradientDrawable();
gradiant_withStroke.setColor(mStrokePaint.getColor());
gradiant_withStroke.setStroke(0, Color.parseColor("#00000000"));
mView.setBackgroundDrawable(gradiant_withStroke);
}
}
and activity_main.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"
android:gravity="center_vertical">
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/view2"
android:layout_gravity="center_horizontal"
android:text="Ripple Tuch"
app:MehDi_rippleColor="#cf030a"
app:MehDi_buttonColor="#ff2c25"
/>
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/view34"
android:text="Produce By"
app:MehDi_rippleColor="#f7f336"
app:MehDi_buttonColor="#d3ce00"
/>
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/view"
android:layout_gravity="center_horizontal"
android:text="MehDi"
app:MehDi_rippleColor="#00930e"
app:MehDi_buttonColor="#00b909"
/>
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/view3"
android:layout_gravity="center_horizontal"
android:text="NazaRi"
app:MehDi_rippleColor="#303dd3"
app:MehDi_buttonColor="#7688e5"
/>
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/view23"
android:layout_gravity="center_horizontal"
android:text="؛)"
app:MehDi_rippleColor="#cf0584"
app:MehDi_buttonColor="#ff0fb6"
/>
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/viehw23"
android:layout_gravity="center_horizontal"
android:text=":D"
app:MehDi_rippleColor="#a2a3a2"
app:MehDi_buttonColor="#dadbda"
/>
You need to dig out the Handler which executes most of these animation calls, and invoke handler.removeCallbacksAndMessages(null); and that will take care of some of the lag.
Because most probably, when you finish and reenter the said activity, it works good and fast as when first ran. This can be caused by having alot of Threads or Handler messages that have ended their lifecycle but haven't been removed due to being dead.

Sliding navigation with half screen

i want to create one screen for my android application in this application i want to create sliding navigation feature which is full visible on swipe (Left to Right ) and when user again swipe (RIght to Left ) it again swipe and close navigation window half and display half screen , i have used navigation drawer but i am not aware with this so please some help me if you have any code so please send me its will be my pleasure .
i am sending one image for reference where .
Find below code create a class AnimationLayout
import usb.terminal.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
public class AnimationLayout extends ViewGroup {
public final static int DURATION = 1000;
protected boolean mPlaceLeft = true;
public boolean mOpened;
protected View mSidebar;
protected View mContent;
protected int mSidebarWidth = 100; /*
* assign default value. It will be
* overwrite in onMeasure by Layout xml
* resource.
*/
protected Animation mAnimation;
protected OpenListener mOpenListener;
protected CloseListener mCloseListener;
protected Listener mListener;
protected boolean mPressed = false;
public AnimationLayout(Context context) {
this(context, null);
}
public AnimationLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onFinishInflate() {
super.onFinishInflate();
mSidebar = findViewById(R.id.animation_layout_sidebar);
mContent = findViewById(R.id.animation_layout_content);
if (mSidebar == null) {
throw new NullPointerException("no view id = animation_sidebar");
}
if (mContent == null) {
throw new NullPointerException("no view id = animation_content");
}
mOpenListener = new OpenListener(mSidebar, mContent);
mCloseListener = new CloseListener(mSidebar, mContent);
}
#Override
public void onLayout(boolean changed, int l, int t, int r, int b) {
/* the title bar assign top padding, drop it */
int sidebarLeft = l;
if (!mPlaceLeft) {
sidebarLeft = r - mSidebarWidth;
}
mSidebar.layout(sidebarLeft, 0, sidebarLeft + mSidebarWidth,
0 + mSidebar.getMeasuredHeight());
if (mOpened) {
if (mPlaceLeft) {
mContent.layout(l + mSidebarWidth, 0, r + mSidebarWidth, b);
} else {
mContent.layout(l - mSidebarWidth, 0, r - mSidebarWidth, b);
}
} else {
mContent.layout(l, 0, r, b);
}
}
#Override
public void onMeasure(int w, int h) {
super.onMeasure(w, h);
super.measureChildren(w, h);
mSidebarWidth = mSidebar.getMeasuredWidth();
}
#Override
protected void measureChild(View child, int parentWSpec, int parentHSpec) {
/* the max width of Sidebar is 90% of Parent */
if (child == mSidebar) {
int mode = MeasureSpec.getMode(parentWSpec);
int width = (int) (getMeasuredWidth() * 0.9);
super.measureChild(child, MeasureSpec.makeMeasureSpec(width, mode),
parentHSpec);
} else {
super.measureChild(child, parentWSpec, parentHSpec);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!isOpening()) {
return false;
}
int action = ev.getAction();
if (action != MotionEvent.ACTION_UP
&& action != MotionEvent.ACTION_DOWN) {
return false;
}
/*
* if user press and release both on Content while sidebar is opening,
* call listener. otherwise, pass the event to child.
*/
int x = (int) ev.getX();
int y = (int) ev.getY();
if (mContent.getLeft() < x && mContent.getRight() > x
&& mContent.getTop() < y && mContent.getBottom() > y) {
if (action == MotionEvent.ACTION_DOWN) {
mPressed = true;
}
if (mPressed && action == MotionEvent.ACTION_UP
&& mListener != null) {
mPressed = false;
return mListener.onContentTouchedWhenOpening();
}
} else {
mPressed = false;
}
return false;
}
public void setListener(Listener l) {
mListener = l;
}
/* to see if the Sidebar is visible */
public boolean isOpening() {
return mOpened;
}
public void toggleSidebar() {
if (mContent.getAnimation() != null) {
return;
}
if (mOpened) {
/* opened, make close animation */
if (mPlaceLeft) {
mAnimation = new TranslateAnimation(0, -mSidebarWidth, 0, 0);
} else {
mAnimation = new TranslateAnimation(0, mSidebarWidth, 0, 0);
}
mAnimation.setAnimationListener(mCloseListener);
} else {
/* not opened, make open animation */
if (mPlaceLeft) {
mAnimation = new TranslateAnimation(0, mSidebarWidth, 0, 0);
} else {
mAnimation = new TranslateAnimation(0, -mSidebarWidth, 0, 0);
}
mAnimation.setAnimationListener(mOpenListener);
}
mAnimation.setDuration(DURATION);
mAnimation.setFillAfter(true);
mAnimation.setFillEnabled(true);
mContent.startAnimation(mAnimation);
}
public void openSidebar() {
if (!mOpened) {
toggleSidebar();
}
}
public void closeSidebar() {
if (mOpened) {
toggleSidebar();
}
}
class OpenListener implements Animation.AnimationListener {
View iSidebar;
View iContent;
OpenListener(View sidebar, View content) {
iSidebar = sidebar;
iContent = content;
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
iSidebar.setVisibility(View.VISIBLE);
}
public void onAnimationEnd(Animation animation) {
iContent.clearAnimation();
mOpened = !mOpened;
requestLayout();
if (mListener != null) {
mListener.onSidebarOpened();
}
}
}
class CloseListener implements Animation.AnimationListener {
View iSidebar;
View iContent;
CloseListener(View sidebar, View content) {
iSidebar = sidebar;
iContent = content;
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
}
public void onAnimationEnd(Animation animation) {
iContent.clearAnimation();
iSidebar.setVisibility(View.INVISIBLE);
mOpened = !mOpened;
requestLayout();
if (mListener != null) {
mListener.onSidebarClosed();
}
}
}
public interface Listener {
public void onSidebarOpened();
public void onSidebarClosed();
public boolean onContentTouchedWhenOpening();
}
}
create a mainlayout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<yourpackage..AnimationLayout
android:id="#+id/animation_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!--Create you Layout which you want and just call in this include-->
<include
android:id="#id/animation_layout_sidebar"
android:layout_width="470dp"
android:layout_height="match_parent"
layout="#layout/my_cook_menu"
android:orientation="vertical" >
</include>
<!--Create you Layout which you want and just call in this include-->
<include
android:id="#id/animation_layout_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
layout="#layout/sound_my_cook"
android:clickable="true"
android:focusable="true"
android:orientation="vertical" >
</include>
</yourpackage.AnimationLayout>
</RelativeLayout>
</RelativeLayout>
create id.xml in values folder
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="animation_layout_sidebar" />
<item type="id" name="animation_layout_content" />
</resources>
write this code in which activity you are calling
public class MainActivity extends Activity implements
AnimationLayout.Listener, OnClickListener{
AnimationDrawable frameAnimation;
AnimationLayout animationLayout;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.mainlayout);
}
public void onClick(View v) {
try {
animationLayout = (AnimationLayout) findViewById(R.id.animation_layout);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
animationLayout.toggleSidebar();
}
#Override
public void onSidebarOpened() {
// TODO Auto-generated method stub
}
#Override
public void onSidebarClosed() {
// TODO Auto-generated method stub
}
#Override
public boolean onContentTouchedWhenOpening() {
// TODO Auto-generated method stub
return false;
}
}
I haven't tried it yet but I think DrawerLayout recently added to support library could give you what you need. Here is the link for documentation. This is something what Facebook app is using to slide a pannel from left to right. Here is the tutorial.

Categories

Resources