Custom view doesn't work well on tablets

I have a custom view being added to android.R.id.content in an Activity. On phones and some tablets, it works fine, but on 10-inch tablets, it unexpectedly lags and works.

Initialization and invocation:

SlideDialog slideDialog = new SlideDialog(MyActivity.this, editText);
    slideDialog.setOnDismissSlideDialog(this);
    slideDialog.showDialog();
    getSupportFragmentManager()
        .beginTransaction()
        .replace(slideDialog.getContentId(),new MyFragment()).commit;

      

This view contains two classes, one interface and one XML file. Here

OnDismissSlideDialog

public interface OnDismissSlideDialog {
  void dismiss();
}

      

SlideDialogClass

public class SlideDialog implements
        View.OnClickListener,
        SlideLayout.OnSlideHeightChangeListener,
        SlideLayout.OnRemove,
        ValueAnimator.AnimatorUpdateListener {

      private static final String LOG_TAG = Logger.getLogTag(SlideDialog.class);

      private static final Float BASE_SHADOW = 0.5f;

      private Activity activity;
      private Context context;
      private ViewGroup rootView;
      private SlideLayout content;
      private LinearLayout contentRoot;
      private OnDismissSlideDialog onDismissSlideDialog;
      private EditText editText;

      public SlideDialog(Activity activity, @Nullable EditText editText) {
        this.activity = activity;
        this.context = activity.getApplicationContext();
        this.editText = editText;
        initDialog();
      }

      public void showDialog() {
        content.setVisibility(View.INVISIBLE);
        showContentRoot();
        if (editText != null) {
          InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(
              Context.INPUT_METHOD_SERVICE);
          inputMethodManager.hideSoftInputFromWindow(editText.getWindowToken(), 0);
        }
      }

      public void setOnDismissSlideDialog(OnDismissSlideDialog onDismissSlideDialog) {
        this.onDismissSlideDialog = onDismissSlideDialog;
      }

      public int getContentId() {
        return content.getContentId();
      }

      private void showContentRoot() {
        rootView.addView(contentRoot);
        slideInContent();
      }

      private void removeContentRootWithAnimation() {
        slideOutContent();
      }

      private void removeContentRoot() {
        rootView.removeView(contentRoot);
        setTransparentStatusBar(activity, context.getResources().getColor(R.color.colorPrimaryDark));
        if (onDismissSlideDialog != null) {
          onDismissSlideDialog.dismiss();
        }
        activity = null;
      }

      private void slideInContent() {
        content.setVisibility(View.VISIBLE);
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(content, "translationY", rootView.getMeasuredHeight() / 2, 0);
        objectAnimator.setDuration(context.getResources().getInteger(android.R.integer.config_shortAnimTime));
        objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        objectAnimator.addUpdateListener(this);
        objectAnimator.start();
      }

      private void slideOutContent() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(content, "translationY", content.getTranslationY(), rootView.getMeasuredHeight() / 2);
        objectAnimator.setDuration(context.getResources().getInteger(android.R.integer.config_shortAnimTime));
        objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        objectAnimator.addListener(new Animator.AnimatorListener() {
          @Override
          public void onAnimationStart(Animator animation) {

          }

          @Override
          public void onAnimationEnd(Animator animation) {
            removeContentRoot();
          }

          @Override
          public void onAnimationCancel(Animator animation) {

          }

          @Override
          public void onAnimationRepeat(Animator animation) {

          }
        });
        objectAnimator.addUpdateListener(this);
        objectAnimator.start();
      }

      private void initDialog() {
        View baseView = activity.getWindow().getDecorView().getRootView();
        rootView = (ViewGroup) baseView.findViewById(android.R.id.content);
        contentRoot = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.slide_dialog, null, false);
        content = (SlideLayout) contentRoot.findViewById(R.id.slide_content);
        content.setOnSlideHeightChangeListener(this);
        content.setRemoveListener(this);
        contentRoot.setOnClickListener(this);
        content.setOnClickListener(this);
      }

      private int adjustAlpha(int color, float factor) {
        int alpha = Math.round(Color.alpha(color) * factor);
        int red = Color.red(color);
        int green = Color.green(color);
        int blue = Color.blue(color);
        return Color.argb(alpha, red, green, blue);
      }

      private int darker(int color, float factor) {
        int a = Color.alpha(color);
        int r = Color.red(color);
        int g = Color.green(color);
        int b = Color.blue(color);

        return Color.argb(a,
            Math.max((int) (r * factor), 0),
            Math.max((int) (g * factor), 0),
            Math.max((int) (b * factor), 0));
      }

      @Override
      public void onClick(View v) {
        if (v.getId() == contentRoot.getId()) {
          dismissDialog();
        }
      }

      @TargetApi(Build.VERSION_CODES.LOLLIPOP)
      public void setTransparentStatusBar(Activity activity, int color) {
        if (activity != null) {
          if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.getWindow().setStatusBarColor(color);
          }
        }
      }

      public void dismissDialog() {
        removeContentRootWithAnimation();
      }

      @Override
      public void onHeightChange(int currentHeight, int maxHeight) {
        float alpha = (float) ((currentHeight * 100) / maxHeight) / 100;
        contentRoot.setBackgroundColor(adjustAlpha(context.getResources().getColor(android.R.color.black), alpha));
        setTransparentStatusBar(activity, darker(context.getResources().getColor(R.color.colorPrimaryDark), 1 - alpha));
      }

      @Override
      public void onRemove() {
        removeContentRoot();
      }

      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        Float value = (Float) animation.getAnimatedValue();
        if (content.getMeasuredHeight() > 0) {
          value = ((value * 100) / content.getMeasuredHeight()) / 100;
          value = (value / 2 + BASE_SHADOW);
          contentRoot.setBackgroundColor(adjustAlpha(context.getResources().getColor(android.R.color.black), 1 - value));
          setTransparentStatusBar(activity, darker(context.getResources().getColor(R.color.colorPrimaryDark), value));
        }
      }
    }

      

SlideLayout

public class SlideLayout extends LinearLayout implements View.OnTouchListener {

  private static final String LOG_TAG = Logger.getLogTag(SlideLayout.class);

  private static final int VELOCITY_UNIT = 200;
  private static final int MAX_VELOCITY = 1000;
  private static final int SHORT_ANIMATION = 175;

  private VelocityTracker mVelocityTracker = null;
  private OnSlideHeightChangeListener onSlideHeightChangeListener;
  private OnRemove removeListener;
  private boolean isMeasured = false;
  private int height;
  private int rootHeight;
  private int coordinateMoveY;
  private int coordinateDownY;
  private float velocity;
  private boolean isAnimated = false;
  private FrameLayout container;

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    init(context);
  }

  public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);
  }

  public SlideLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
  }

  public SlideLayout(Context context) {
    super(context);
    init(context);
  }

  public void init(Context context) {
    setOrientation(VERTICAL);

    ImageView slidePanel = new ImageView(context);
    container = new FrameLayout(context);

    FrameLayout.LayoutParams containerParams  = new FrameLayout.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.MATCH_PARENT
    );

    ViewGroup.LayoutParams slidePanelParams = new ViewGroup.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.WRAP_CONTENT
    );

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
      container.setId(generateViewId());
    } else {
      container.setId(android.R.id.custom);
    }

    container.setLayoutParams(containerParams);
    slidePanel.setLayoutParams(slidePanelParams);

    slidePanel.setBackgroundColor(getResources().getColor(android.R.color.white));
    slidePanel.setImageResource(R.mipmap.ic_lines);

    addView(slidePanel);
    addView(container);
    slidePanel.setOnTouchListener(this);
  }

  public int getContentId() {
    return container.getId();
  }

  @Override
  public void setTranslationY(float translationY) {
    super.setTranslationY(translationY);
  }

  public void setOnSlideHeightChangeListener(OnSlideHeightChangeListener onSlideHeightChangeListener) {
    this.onSlideHeightChangeListener = onSlideHeightChangeListener;
  }

  public void setRemoveListener(OnRemove removeListener) {
    this.removeListener = removeListener;
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if (!isMeasured) {
      if (getMeasuredHeight() > height) {
        height = getMeasuredHeight();
          rootHeight = (height * 2);
      }
    }
  }

  @Override
  public void draw(Canvas canvas) {
    super.draw(canvas);
  }

  private void setHeight(int coordinateY) {
    setLayoutParams(
        new LinearLayout.LayoutParams(
            new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                coordinateY)
        )
    );
  }

  private void slideToEnd(final int fromY, int toY, int velocity) {
    ValueAnimator valueAnimator = ValueAnimator.ofInt(fromY, 0);
    valueAnimator.setDuration(velocity);
    valueAnimator.setInterpolator(new AccelerateInterpolator());
    valueAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
        isAnimated = true;
      }

      @Override
      public void onAnimationEnd(Animator animation) {
        isAnimated = false;
        removeListener.onRemove();
      }

      @Override
      public void onAnimationCancel(Animator animation) {

      }

      @Override
      public void onAnimationRepeat(Animator animation) {

      }
    });
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        Integer value = (Integer) animation.getAnimatedValue();
        setTranslationY(fromY - value);
        onSlideHeightChangeListener.onHeightChange(value, rootHeight);
      }
    });
    valueAnimator.start();
  }

  private void slideToUp(int fromY, int toY, int velocity) {
    ValueAnimator valueAnimator = ValueAnimator.ofInt(fromY, toY);
    valueAnimator.setDuration(velocity);
    valueAnimator.setInterpolator(new AccelerateInterpolator());
    valueAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
        isAnimated = true;
      }

      @Override
      public void onAnimationEnd(Animator animation) {
        isAnimated = false;
      }

      @Override
      public void onAnimationCancel(Animator animation) {

      }

      @Override
      public void onAnimationRepeat(Animator animation) {

      }
    });
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        Integer value = (Integer) animation.getAnimatedValue();
        setHeight(value);
        onSlideHeightChangeListener.onHeightChange(value, rootHeight);
      }
    });
    valueAnimator.start();
  }

  private int measureCoordinateY(float rawCoordinateY) {
    int measuredCoordinateY;
    if (rawCoordinateY > 0) {
      measuredCoordinateY = getMeasuredHeight() - (int) (rawCoordinateY);
    } else {
      measuredCoordinateY = getMeasuredHeight() + (int) (Math.abs(rawCoordinateY));
    }

    return measuredCoordinateY;
  }

  private int getAnimationDuration(float velocity) {
    int animationSpeedDifference =
        getResources()
            .getInteger(android.R.integer.config_longAnimTime) - SHORT_ANIMATION;

    return (int) (getResources()
        .getInteger(android.R.integer.config_longAnimTime) - (velocity / MAX_VELOCITY) * animationSpeedDifference);
  }

  public interface OnSlideHeightChangeListener {
    void onHeightChange(int currentHeight, int maxHeight);
  }

  public interface OnRemove {
    void onRemove();
  }

  public int getStatusBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
      result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
  }

  @Override
  public boolean onTouch(View v, MotionEvent event) {
    int index = event.getActionIndex();
    int pointerId = event.getPointerId(index);

    if (!isAnimated) {
      switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
          if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
          } else {
            mVelocityTracker.clear();
          }
          coordinateDownY = measureCoordinateY(event.getY());
          mVelocityTracker.addMovement(event);
          isMeasured = true;
          break;
        case MotionEvent.ACTION_UP:
          if (coordinateMoveY != 0) {
            if (coordinateDownY < coordinateMoveY) {
              slideToUp(coordinateMoveY, rootHeight, getAnimationDuration(velocity));
            } else {
              slideToEnd(coordinateMoveY, 0, getAnimationDuration(velocity));
            }
          }
          return true;
        case MotionEvent.ACTION_MOVE:
          coordinateMoveY = measureCoordinateY(event.getY());
          setHeight(coordinateMoveY);

          if (coordinateMoveY < 0) {
            removeListener.onRemove();
          }

          onSlideHeightChangeListener.onHeightChange(coordinateMoveY, rootHeight);

          mVelocityTracker.addMovement(event);
          mVelocityTracker.computeCurrentVelocity(VELOCITY_UNIT, MAX_VELOCITY);

          velocity = Math.abs(VelocityTrackerCompat.getYVelocity(mVelocityTracker, pointerId));
          return true;
        case MotionEvent.ACTION_CANCEL:
          mVelocityTracker.recycle();
          break;
      }
    }
    return super.onTouchEvent(event);
  }
}

      

XML file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/decor_content_parent"
              xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

  <Space
      android:layout_width="match_parent"
      android:layout_height="0dp"
      android:layout_weight="1"/>

  <SlideLayout
      android:id="@+id/slide_content"
      android:layout_width="match_parent"
      android:layout_height="0dp"
      android:layout_weight="1"
      android:background="@color/white">
  </SlideLayout>

</LinearLayout>

      

+3


source to share





All Articles