test1

Function implementation

Click stretch control demand is still very common, generally TextView expansion, because there may be too much text to display, one-time expansion affects the user’s physical strength, so give the option to the user, of course, there will be performance optimization considerations, here I give three different ways to achieve the above requirements.

// 1. Use Gong & Visible for effect. Advantages: simple; Disadvantages: Waste of resources.
    public void setViewIsVisibility(View view) {if(view= =null) return;

        if (view.getVisibility() ! =View.VISIBLE) {
            view.setVisibility(View.VISIBLE);
        } else{
            view.setVisibility(View.GONE); }}Copy the code

The first implementation is as simple as setting the control to disable in XML and then reversing the control state in the listener method.

// 2. Lazy load layout with ViewStub, [used in conjunction with the first method.]
        if(viewStub.getVisibility() ! =View.VISIBLE){
            viewStub.setVisibility(View.VISIBLE);
            llt = ((LinearLayout) findViewById(R.id.llt));
        }else {
            setViewIsVisibility(llt);
        }Copy the code

The second implementation is the same as the first, but more performance efficient, using a control called the ViewStub, which is zero in width and height, Android: Layout =”@layout/ layout_ZOMm_content “will load the View from android:layout=”@layout/layout_zomm_content”. When loaded, the getVisibility() method returns 0. INVISIBLE = 0x00000004; VISIBLE = 0x00000000; GONE = 0x00000008; So we use a combination to achieve the stretching effect.





test2.g

// 3. Add animation effect, the idea is as follows: get control height, according to the control height value animation change layout height.
if(mHeight < 0){
  mHeight= getViewHeight(); // Since we are writing at the height of 0 in XML, we need to remeasure it.} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --private void executeAnimation(a) {
  if(mHeight < 0)  return;     
  if(llt.getHeight() ! =0){
    p = mHeight;
    s = 0;
   }else {
    s = mHeight;
    p = 0;
  }

  final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) llt.getLayoutParams();
  ValueAnimator animator = ValueAnimator.ofInt(p,s);

// This method is called whenever the user clicks, so don't create it as an inner class. I'm just saving code and making it easier to read.
  animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
    public void onAnimationUpdate(ValueAnimator animation) {

      int animatedValue = (int) animation.getAnimatedValue(); lp.height = animatedValue; llt.setLayoutParams(lp); }}); animator.setDuration(500);
  animator.start();
}Copy the code

The third way would have been to set the layout to GONE in XML and get the height of the layout, animate values based on the height, but set the layout to 0 after GONE. Because it could not be achieved so we had to re-measure the height of the control, the measurement code is as follows.

public int getViewHeight(){
  llt.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
  int width = llt.getLayoutParams().width;
  int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.AT_MOST);
  int widthMakeMeasureSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
  llt.measure(widthMakeMeasureSpec, heightMeasureSpec);
  mHeight = llt.getMeasuredHeight();
}Copy the code

Actually, writing measure(0, 0) can also get the height of the control, but I think as an aspiring programmer, it’s better to figure out why.

The View of measurement

Because the height of the control is set to 0dp in the layout file, So the first thing we’re going to do is we’re going to change 0dp, and the reason is also related to the measurement of the view, because the width and height of the view is affected by everything and when we set the view to match_parent the width and height of the view is affected by the parent control, and when we set the view to wrap_content it’s affected by the child view, Only when the width of the View is written to death can it take charge of its own, based on the complexity of the View width and height designer of the final View for a double judgment, the code is as follows:

private int getRootMeasureSpec(int windowSize, int rootDimension) {  
    int measureSpec;  
    switch (rootDimension) {  
    case ViewGroup.LayoutParams.MATCH_PARENT:  
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);  
        break;  
    case ViewGroup.LayoutParams.WRAP_CONTENT:  
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);  
        break;  
    default:  
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);  
        break;  
    }  
    return measureSpec;  
}Copy the code

This method is called by the outermost FrameLayout, and the outermost View width is definitely accurate, covering the entire screen. If the screen is 480*320 then windowSize is one of those two values and what’s the rootDimension? This is the value of the layout property layout_widht that we wrote in the XML.

childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);  
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);Copy the code

As MeasureSpec’s three constants are combined with the layout_XXX property, we can see that the return value is MeasureSpec’s three constants and the layout_xxx property.

  • MeasureSpec.EXACTLY specifies that the parent expects the size of the child view to be determined by the value of specSize. This is the default rule for setting the child size, and developers can set it to any size they wish.
  • MeasureSpec.AT_MOST specifies that a child view must be at most the size specified in specSize. Developers should set this view as small as possible and ensure that it does not exceed specSize. This rule is used by default to set the size of the subview, but developers can set it to any size they wish.
  • MeasureSpec.UNSPECIFIED means that developers can set the view to any size they wish, with no restrictions. This is a rare situation, not used very often.

MeasureSpec.EXACTLY = MeasureSpec.EXACTLY = MeasureSpec.EXACTLY = MeasureSpec.EXACTLY = MeasureSpec.EXACTLY = MeasureSpec.

  llt.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
  int width = llt.getLayoutParams().width;
  int widthMakeMeasureSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
  llt.measure(widthMakeMeasureSpec, heightMeasureSpec);Copy the code

The measure() method calls the onMeasure() method, and we’re definitely not going to be able to do that, because it’s an abstract method, so I’m going to have to override that method when I inherit the View. The onMeasure() method will eventually call getDefaultSize(), which will parse our previous values.

public static int getDefaultSize(int size.int measureSpec) {  
    int result = size;  
    int specMode = MeasureSpec.getMode(measureSpec);  
    int specSize = MeasureSpec.getSize(measureSpec);  
    switch (specMode) {  
    case MeasureSpec.UNSPECIFIED:  
        result = size;  
        break;  
    case MeasureSpec.AT_MOST:  
    case MeasureSpec.EXACTLY:  
        result = specSize;  
        break;  
    }  
    return result;  
}Copy the code

The final return value of AT_MOST and EXACTLY is the value in measureSpec. If measureSpec consists of measureSpec.EXACTLY and the exact value (if = 480) then the final return value is 480. Just like the width we measure, because getRootMeasureSpec(desiredWindowWidth, LP.width); The 480 that comes in and we’re going to be using match_parnet all the time so the 480 that comes out is going to wrap the whole screen. Wait, but how did you measure the height and pass in 1000 end up as the contents of the package, and why does passing in 0 have the same effect? So we need to see where getDefaultSize() is called

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --protected int getSuggestedMinimumWidth(a) {
        return (mBackground == null)? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); }Copy the code

Still nothing!! The reason is that we are not looking at the onMeasure method of the View at this time, but the onMeasure method of the specific subclass. When we customize the View, we also measure our View in the onMeasure method. What does the passed 0 mean? So what’s the pattern of 0

public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY     = 1 << MODE_SHIFT;   
public static final int AT_MOST     = 2 << MODE_SHIFT;Copy the code

As you can see, the passed in 0 corresponds to the UNSPECIFIED mode, and the subclasses retrieve the mode in the UNSPECIFIED statement. How does a ViewGroup measure subclasses? It calls the following method.

WidthMeasureSpec and heightMeasureSpec are the parent View's own combined values.
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {  
    final int size = mChildrenCount;  
    final View[] children = mChildren;  
    for (int i = 0; i < size; ++i) {  
        final View child = children[i];  
        // This explains why GONEView does not hold space and cannot get height.
        if((child.mViewFlags & VISIBILITY_MASK) ! = GONE) {// The width of a son is influenced by his father.measureChild(child, widthMeasureSpec, heightMeasureSpec); }}} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --protected void measureChild(View child, int parentWidthMeasureSpec,  
        int parentHeightMeasureSpec) {  
    final LayoutParams lp = child.getLayoutParams();  
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,  
            mPaddingLeft + mPaddingRight, lp.width);  
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,  
            mPaddingTop + mPaddingBottom, lp.height);  
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
}Copy the code