我有一个自定义View,它被添加到Activity中的android.R.id.content中。 在手机和平板电脑上,它可以正常工作,但在10英寸屏幕平板电脑上,它会滞后并意外地工作。
初始化和调用视图:
SlideDialog slideDialog = new SlideDialog(MyActivity.this, editText);
slideDialog.setOnDismissSlideDialog(this);
slideDialog.showDialog();
getSupportFragmentManager()
.beginTransaction()
.replace(slideDialog.getContentId(),new MyFragment()).commit;
此视图包含两个类,一个接口和一个XML文件。 这是
public interface OnDismissSlideDialog {
void dismiss();
}
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));
}
}
}
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 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>