<merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageButton style="@style/ImageButton" android:id="@+id/newTabButton" android:layout_width="@dimen/toolbar_button_size" android:layout_height="@dimen/toolbar_button_size" android:layout_gravity="start" android:contentDescription="@string/content_desc_add_tab" android:src="@drawable/ic_plus" /> <Button android:id="@+id/tabSwitcher" android:layout_width="@dimen/toolbar_button_size" android:layout_height="@dimen/toolbar_button_size" android:layout_gravity="end" android:enabled="false" /> <com.bejibx.webviewexample.widget.UrlBar android:id="@+id/urlContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="5dp" android:freezesText="true" android:hint="@string/hint_url_container" android:imeOptions="actionGo|flagNoExtractUi|flagNoFullscreen" android:inputType="textUri" android:paddingLeft="8dp" android:paddingRight="8dp" android:singleLine="true" android:visibility="gone" /> </merge>
public class ToolbarLayout extends ViewGroup { private static final String TAG = ToolbarLayout.class.getSimpleName(); private static final boolean DEBUG = true; private ImageButton mNewTabButton; private Button mTabSwitchButton; private UrlBar mUrlContainer; public ToolbarLayout(Context context) { super(context); initializeViews(context); } public ToolbarLayout(Context context, AttributeSet attrs) { super(context, attrs); initializeViews(context); } public ToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initializeViews(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public ToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initializeViews(context); } private void initializeViews(Context context) { LayoutInflater.from(context).inflate(R.layout.fragment_address_bar_template, this, true); mUrlContainer = (UrlBar) findViewById(R.id.urlContainer); mNewTabButton = (ImageButton) findViewById(R.id.newTabButton); mTabSwitchButton = (Button) findViewById(R.id.tabSwitcher); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (DEBUG) { Log.d(TAG, LogHelper.onMeasure(widthMeasureSpec, heightMeasureSpec)); } int widthConstrains = getPaddingLeft() + getPaddingRight(); final int heightConstrains = getPaddingTop() + getPaddingBottom(); int totalHeightUsed = heightConstrains; int childTotalWidth; int childTotalHeight; MarginLayoutParams lp; measureChildWithMargins( mNewTabButton, widthMeasureSpec, widthConstrains, heightMeasureSpec, heightConstrains); lp = (MarginLayoutParams) mNewTabButton.getLayoutParams(); childTotalWidth = mNewTabButton.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; childTotalHeight = mNewTabButton.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; widthConstrains += childTotalWidth; totalHeightUsed += childTotalHeight; measureChildWithMargins( mTabSwitchButton, widthMeasureSpec, widthConstrains, heightMeasureSpec, heightConstrains); lp = (MarginLayoutParams) mTabSwitchButton.getLayoutParams(); childTotalWidth = mTabSwitchButton.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; childTotalHeight = mTabSwitchButton.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; widthConstrains += childTotalWidth; totalHeightUsed = Math.max(childTotalHeight + heightConstrains, totalHeightUsed); /* * [FIXED] find out how to handle match_parent here * There was not a problem with match_parent interaction here. The real problem is * layout_height="wrap_content" on high-level container cause EditText to measure it's * height improperly. For now I'm just set layout_height on high-level layout to fixed value * (this make sense because of top-level layout structure, see activity_main.xml) which * measure EditText correctly. * * TODO I'm steel need to figure out whats going wrong in this particular case. */ if (mUrlContainer.getVisibility() != GONE) { measureChildWithMargins( mUrlContainer, widthMeasureSpec, widthConstrains, heightMeasureSpec, heightConstrains); lp = (MarginLayoutParams) mUrlContainer.getLayoutParams(); childTotalWidth = mUrlContainer.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; childTotalHeight = mUrlContainer.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; widthConstrains += childTotalWidth; totalHeightUsed = Math.max(childTotalHeight + heightConstrains, totalHeightUsed); } final int totalWidthUsed = widthConstrains; setMeasuredDimension( resolveSize(totalWidthUsed, widthMeasureSpec), resolveSize(totalHeightUsed, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int parentLeft, int parentTop, int parentRight, int parentBottom) { if (DEBUG) { Log.d(TAG, LogHelper.onLayout(changed, parentLeft, parentTop, parentRight, parentBottom)); } /* * Layout order: * 1. Layout "New tab" button on the left side. * 2. Layout "Tab switch" button on the right side. * 3. If url container is unfocused, layout it between "New tab" and "Tab switch" buttons. * Otherwise layout it accordingly to mUrlContainerExpandedRect bounds. */ int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); /* * Edges for url container left and right bounds. Move it during layout childs * located to right and left of url container. */ int leftEdge = parentLeft + paddingLeft; int rightEdge = parentRight - paddingRight; int childLeft, childTop, childRight, childBottom, childWidth, childHeight; if (mNewTabButton.getVisibility() != GONE) { MarginLayoutParams lp = (MarginLayoutParams) mNewTabButton.getLayoutParams(); childWidth = mNewTabButton.getMeasuredWidth(); childHeight = mNewTabButton.getMeasuredHeight(); childLeft = parentLeft + paddingLeft + lp.leftMargin; childTop = parentTop + paddingTop + lp.topMargin; childRight = childLeft + childWidth; childBottom = childTop + childHeight; mNewTabButton.layout(childLeft, childTop, childRight, childBottom); leftEdge = childRight + lp.rightMargin; } if (mTabSwitchButton.getVisibility() != GONE) { MarginLayoutParams lp = (MarginLayoutParams) mTabSwitchButton.getLayoutParams(); childWidth = mTabSwitchButton.getMeasuredWidth(); childHeight = mTabSwitchButton.getMeasuredHeight(); childRight = parentRight - paddingRight - lp.rightMargin; childTop = parentTop + paddingTop + lp.topMargin; childLeft = childRight - childWidth; childBottom = childTop + childHeight; mTabSwitchButton.layout(childLeft, childTop, childRight, childBottom); rightEdge = childLeft - lp.leftMargin; } if (mUrlContainer.getVisibility() != GONE) { MarginLayoutParams lp = (MarginLayoutParams) mUrlContainer.getLayoutParams(); childHeight = mUrlContainer.getMeasuredHeight(); childLeft = leftEdge + lp.leftMargin; childTop = parentTop + paddingTop + lp.topMargin; childRight = rightEdge - lp.rightMargin; childBottom = childTop + childHeight; mUrlContainer.layout(childLeft, childTop, childRight, childBottom); } } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } @Override protected LayoutParams generateLayoutParams(LayoutParams p) { return new MarginLayoutParams(p); } @Override protected LayoutParams generateDefaultLayoutParams() { return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @Override protected void measureChildWithMargins( @NonNull View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams(); int childWidthMeasureSpec = getChildMeasureSpec( parentWidthMeasureSpec, widthUsed + layoutParams.leftMargin + layoutParams.rightMargin, layoutParams.width); int childHeightMeasureSpec = getChildMeasureSpec( parentHeightMeasureSpec, heightUsed + layoutParams.topMargin + layoutParams.bottomMargin, layoutParams.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } }
private static final float URL_FOCUS_CHANGE_FOCUSED_PERCENT = 1.0f; private static final float URL_FOCUS_CHANGE_UNFOCUSED_PERCENT = 0.0f; /** * 1.0 is 100% focused, 0 is unfocused */ private float mUrlFocusChangePercent;
/** * Use actual property to avoid reflection when creating animators. For api from * 11 (3.0.X Honeycomb) to 13 (3.2 Honeycomb_mr2) we should use reflection (see {@link <a href="http://developer.android.com/guide/topics/graphics/prop-animation.html#object-animator">Animating with ObjectAnimator</a>}). * For older apis I'll recommend to use {@link <a href="http://nineoldandroids.com/">NineOldAndroids</a>} library. */ private final Property<ToolbarLayout, Float> mUrlFocusChangePercentProperty = new Property<ToolbarLayout, Float>(Float.class, "") { @Override public void set(ToolbarLayout object, Float value) { mUrlFocusChangePercent = value; mUrlContainer.invalidate(); invalidate(); } @Override public Float get(ToolbarLayout object) { return object.mUrlFocusChangePercent; } };
private boolean mDisableRelayout; private final UrlContainerFocusChangeListener mUrlContainerFocusChangeListener = new UrlContainerFocusChangeListener(); private class UrlContainerFocusChangeListener implements OnFocusChangeListener { @Override public void onFocusChange(View v, boolean hasFocus) { if (DEBUG) { Log.d(TAG, LogHelper.onFocusChange(hasFocus)); } // Trigger url focus animation if (mUrlFocusingLayoutAnimator != null && mUrlFocusingLayoutAnimator.isRunning()) { mUrlFocusingLayoutAnimator.cancel(); mUrlFocusingLayoutAnimator = null; } List<Animator> animators = new ArrayList<>(); Animator animator; if (hasFocus) { animator = ObjectAnimator.ofFloat(this, mUrlFocusChangePercentProperty, URL_FOCUS_CHANGE_FOCUSED_PERCENT); } else { animator = ObjectAnimator.ofFloat(this, mUrlFocusChangePercentProperty, URL_FOCUS_CHANGE_UNFOCUSED_PERCENT); } animator.setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); animators.add(animator); mUrlFocusingLayoutAnimator = new AnimatorSet(); mUrlFocusingLayoutAnimator.playTogether(animators); mUrlFocusingLayoutAnimator.addListener(new UrlFocusingAnimatorListenerAdapter(hasFocus)); mUrlFocusingLayoutAnimator.start(); } } private class UrlFocusingAnimatorListenerAdapter extends AnimatorListenerAdapter { private final boolean mHasFocus; public UrlFocusingAnimatorListenerAdapter(boolean hasFocus) { super(); mHasFocus = hasFocus; } @Override public void onAnimationEnd(Animator animation) { mDisableRelayout = false; if (!hasFocus()) { mTabSwitchButton.setVisibility(VISIBLE); requestLayout(); } } @Override public void onAnimationStart(Animator animation) { if (mHasFocus) { mTabSwitchButton.setVisibility(GONE); requestLayout(); } else { mDisableRelayout = true; } } }
private void initializeViews(Context context) { //... mUrlContainer.setOnFocusChangeListener(mUrlContainerFocusChangeListener); }
private boolean mDisableRelayout; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (!mDisableRelayout) { // ... } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int parentLeft, int parentTop, int parentRight, int parentBottom) { if (!mDisableRelayout) { // ... } }
/** * Rectangle, which represents url container bounds relative to it's * parent bounds when unfocused. */ private final Rect mUrlContainerCollapsedRect = new Rect(); /** * Rectangle, which represents url container bounds relative to it's * parent bounds when FOCUSED. */ private final Rect mUrlContainerExpandedRect = new Rect(); @Override protected void onLayout(boolean changed, int parentLeft, int parentTop, int parentRight, int parentBottom) { //... updateUrlBarCollapsedRect(); /* * UrlBar'. UrlBar ViewGroup. */ mUrlContainerExpandedRect.set(0, 0, parentRight, parentBottom); } /* * UrlBar' . * , . */ private void updateUrlBarCollapsedRect() { int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); int rightEdge = getMeasuredWidth() - paddingRight; MarginLayoutParams lp = (MarginLayoutParams) mTabSwitchButton.getLayoutParams(); rightEdge -= (lp.leftMargin + mTabSwitchButton.getMeasuredWidth() + lp.rightMargin); lp = (MarginLayoutParams) mUrlContainer.getLayoutParams(); int childHeight = mUrlContainer.getMeasuredHeight(); int childLeft = paddingLeft + lp.leftMargin; int childTop = paddingTop + lp.topMargin; int childRight = rightEdge - lp.rightMargin; int childBottom = childTop + childHeight; mUrlContainerCollapsedRect.set(childLeft, childTop, childRight, childBottom); }
<com.bejibx.webviewexample.widget.UrlBar ... android:background="@null" />
private Drawable mUrlContainerBackground; /** * Variable to store url background padding's. This is important when we use * 9-patch as background drawable. */ private final Rect mUrlBackgroundPadding = new Rect(); private void initializeViews(Context context) { //... mUrlContainerBackground = ApiCompatibilityHelper.getDrawable(getResources(), R.drawable.textbox); mUrlContainerBackground.getPadding(mUrlBackgroundPadding); }
@Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (child == mUrlContainer) { boolean clipped = false; if (mUrlContainerBackground != null) { canvas.save(); int clipLeft = mUrlContainerCollapsedRect.left; int clipTop = mUrlContainerCollapsedRect.top; int clipRight = mUrlContainerCollapsedRect.right; int clipBottom = mUrlContainerCollapsedRect.bottom; int expandedLeft = mUrlContainerExpandedRect.left - mUrlBackgroundPadding.left; int expandedTop = mUrlContainerExpandedRect.top - mUrlBackgroundPadding.top; int expandedRight = mUrlContainerExpandedRect.right + mUrlBackgroundPadding.right; int expandedBottom = mUrlContainerExpandedRect.bottom + mUrlBackgroundPadding.bottom; if (mUrlFocusChangePercent == URL_FOCUS_CHANGE_FOCUSED_PERCENT) { clipLeft = expandedLeft; clipTop = expandedTop; clipRight = expandedRight; clipBottom = expandedBottom; } else { // No need to compute those when url bar completely focused or unfocused. int deltaLeft = clipLeft - expandedLeft; int deltaTop = clipTop - expandedTop; int deltaRight = expandedRight - clipRight; int deltaBottom = expandedBottom - clipBottom; clipLeft -= deltaLeft * mUrlFocusChangePercent; clipTop -= deltaTop * mUrlFocusChangePercent; clipRight += deltaRight * mUrlFocusChangePercent; clipBottom += deltaBottom * mUrlFocusChangePercent; } mUrlContainerBackground.setBounds(clipLeft, clipTop, clipRight, clipBottom); mUrlContainerBackground.draw(canvas); canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom); clipped = true; } boolean result = super.drawChild(canvas, mUrlContainer, drawingTime); if (clipped) { canvas.restore(); } return result; } return super.drawChild(canvas, child, drawingTime); }
Source: https://habr.com/ru/post/268889/
All Articles