Let’s start with an effect

Note the following points:
  1. Rounded corners
  2. Press the effect
  3. Same layout, two line effects and one line effects
  4. I have a 1px line on the outside

Perhaps I am uneducated, and finally with the help of others to achieve this effect.

There are several points to pay attention to:

  1. Rounded corners are custom controls
  2. The custom control controls whether there are lines in the outermost layer
  3. Click on the effect of
  4. How do you change a single line to a double line
  5. How to pop up the interface on the window,
The official start of the
  1. Custom controls with rounded corners

    1. Copy available
      public class CornerLinearLayout extends LinearLayout {
          private final RectF roundRect = new RectF();
          private float rect_adius = getResources().getDimension(R.dimen.m2);
          private int borderWidth = 0;
          private final Paint maskPaint = new Paint();
          private final Paint zonePaint = new Paint();
          private Paint borderPaint = null;
      
          public CornerLinearLayout(@NonNull Context context) {
              this(context, null);
          }
      
          public CornerLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
              this(context, attrs, 0);
          }
      
          public CornerLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
              super(context, attrs, defStyleAttr);
              getAttrs(context, attrs);
              init();
          }
      
          private void getAttrs(Context context, AttributeSet attrs) {
              TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CornerLinearLayout);
              rect_adius = ta.getDimension(R.styleable.CornerLinearLayout_cornerRadius, rect_adius);
              borderWidth = ta.getDimensionPixelOffset(R.styleable.CornerLinearLayout_border_width, -1);
              if (BuildConfig.DEBUG) {
                  Log.d("telenewbie::", getClass().getSimpleName() + ",getAttrs" + borderWidth);
              }
      
              ta.recycle();
          }
      
          private void init(a) {
              maskPaint.setAntiAlias(true);
              maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
              zonePaint.setAntiAlias(true);
              zonePaint.setColor(Color.parseColor("# 000000"));
              if (borderWidth > 0) {
                  borderPaint = new Paint();
                  borderPaint.setStrokeWidth(borderWidth);
                  borderPaint.setAntiAlias(true);
                  borderPaint.setStyle(Paint.Style.STROKE);
                  borderPaint.setColor(Color.parseColor("#19FFFFFF"));
              }
      
              float density = getResources().getDisplayMetrics().density;
              rect_adius = rect_adius * density;
          }
      
          @Override
          protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
              super.onLayout(changed, left, top, right, bottom);
              int w = getWidth();
              int h = getHeight();
              roundRect.set(0.0, w, h);
          }
      
          @Override
          public void draw(Canvas canvas) {
              // Create layer A and draw the rounded corner matrix
              canvas.saveLayer(roundRect, zonePaint, Canvas.ALL_SAVE_FLAG);
              canvas.drawRoundRect(roundRect, rect_adius, rect_adius, zonePaint);
              // Create layer B and use xferMode to turn the shape of the layer into a rounded matrix
              canvas.saveLayer(roundRect, maskPaint, Canvas.ALL_SAVE_FLAG);
              // Empty the layer color
              canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
              super.draw(canvas);
              // Draw layer B onto the canvas
              if(borderPaint ! =null) {
                  canvas.drawRoundRect(roundRect, rect_adius, rect_adius, borderPaint);
              }
              canvas.restore();
          }
      
          public void setCorner(float adius) { rect_adius = adius; invalidate(); }}Copy the code
  2. Control lines

    1. Add this to attrs.xml to control the corner size and border size
     <declare-styleable name="CornerLinearLayout">
             <attr name="cornerRadius" />
             <attr name="border_width" />
     </declare-styleable>
    Copy the code
  3. Click on the effect of

    1. At the beginning really did not think of this effect to do, and then the great god said: transparent can. And so it looks like this. base_dialog_btn_bg.xml

      <selector xmlns:android="http://schemas.android.com/apk/res/android">
          <item android:drawable="@color/white_10" android:state_pressed="true" />
          <item android:drawable="@color/transparent" />
      </selector>
      Copy the code
    2. After that, just set the background color for the click range.

      <TextView
              android:id="@+id/tv_cancel"
              android:layout_width="@dimen/m112"
              android:layout_height="match_parent"
              android:background="@drawable/base_dialog_btn_bg"
              android:gravity="center"
              android:textSize="@dimen/base_tv_h5"
              android:text="Cancel" />
      Copy the code
  4. A single line becomes a double line

    1. The way I did it was to hide + resize components

      <com.txznet.music.widget.CornerLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          android:id="@+id/rl_root"
          android:layout_width="match_parent"
          android:layout_height="@dimen/m112"
          android:layout_gravity="center_horizontal"
          android:layout_marginLeft="@dimen/m32"
          android:layout_marginRight="@dimen/m32"
          android:layout_marginTop="@dimen/m16"
          android:orientation="horizontal"
          android:background="@color/base_dialog_bg"
          app:border_width="@dimen/m1"
          app:cornerRadius="@dimen/m8">
          <LinearLayout
              android:layout_width="0dp"
              android:layout_height="match_parent"
              android:layout_weight="1"
              android:orientation="vertical">
              <LinearLayout
                  android:id="@+id/ll_first_range"
                  android:layout_width="match_parent"
                  android:layout_height="0dp"
                  android:layout_weight="1"
                  android:background="@drawable/base_dialog_btn_bg"
                  android:gravity="center_vertical"
                  android:orientation="horizontal">
                  
              </LinearLayout>
      
              <View style="@style/Base_Divider_Horizontal" />
      
              <LinearLayout
                  android:id="@+id/ll_second_range"
                  android:layout_width="match_parent"
                  android:layout_height="0dp"
                  android:layout_weight="1"
                  android:background="@drawable/base_dialog_btn_bg"
                  android:gravity="center_vertical"
                  android:orientation="horizontal"
                  android:visibility="visible">
              </LinearLayout>
      
          </LinearLayout>
          <View style="@style/Base_Divider_Vertical" />
          <TextView
              android:id="@+id/tv_cancel"
              android:layout_width="@dimen/m112"
              android:layout_height="match_parent"
              android:background="@drawable/base_dialog_btn_bg"
              android:gravity="center"
              android:textSize="@dimen/base_tv_h5"
              android:text="Cancel" />
      </com.txznet.music.widget.CornerLinearLayout>
      Copy the code
    2. Then make it a row. Just change the size of the outermost control to the size of the row. For example, the height of the single row is 72 [800×480 devices].

      private void changeLine(int style) {
              ViewGroup.LayoutParams layoutParams = mView.findViewById(R.id.rl_root).getLayoutParams();
              if (layoutParams == null) {
                  layoutParams = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
              }
              if (STYLE_DOUBLE == style) {
                  layoutParams.height = ((int) GlobalContext.get().getResources().getDimension(R.dimen.m112));
              } else if (STYLE_SINGLE == style) {
                  llSecondRange.setVisibility(GONE);
                  layoutParams.height = ((int) GlobalContext.get().getResources().getDimension(R.dimen.m72));
              }
              mView.findViewById(R.id.rl_root).setLayoutParams(layoutParams);
          }
      Copy the code
  5. Add to the main window to the screen

    1. I am a 4.4 device, so the permissions are not considered, because the framework applies for all the 5.0 permissions by default

      mWinManager = (WindowManager) GlobalContext.get().getSystemService(Context.WINDOW_SERVICE);
      mLayoutParam = new WindowManager.LayoutParams();
      mLayoutParam.width = WindowManager.LayoutParams.MATCH_PARENT;
      mLayoutParam.height = WindowManager.LayoutParams.WRAP_CONTENT;
      mLayoutParam.type = WindowManager.LayoutParams.TYPE_PHONE;
      mLayoutParam.flags = 40;
      mLayoutParam.format = PixelFormat.RGBA_8888;
      mLayoutParam.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
      mWinManager.addView(PushNotification.this, mLayoutParam);
      Copy the code

Simple adaptation scheme

  1. You may have noticed that the width and length of the code or XML are set in @dimen/m120, because this is not the toutiao adaptation scheme, but the old scale method, which is to convert the values under different screen resolutions according to the base (800×480), for example: A 1px width above 800×480 equals a few pixels above 1024×600, and a 1px height equals a few pixels above 1024×600?

    H = 1px * 600/480 = 1.25px w = 1px * 1024/800 = 1.28px

    Maybe you will have a question [may have experienced to know, in the car environment inside a variety of strange equipment, impossible to prevent, some length is very long, but the height is very short, some height is very high, but the length is very short], that is not my a square picture will be drawn into a strange long bar. For example, the ratios 1.25 and 1.28 are converted to a minimum resolution. For example, the ratios 1.25 and 1.28 are converted to a minimum resolution. 1.25 and 1.28 we use the 1.25 rule, so we have a file like this: (of course, this file can be automatically generated in Java, just write the rules)

    <?xml version="1.0" encoding="utf-8"? >
    <resources><dimen name="m1">1.25 px.</dimen>
    <dimen name="m2">2.5 px.</dimen>
    <dimen name="m3">3.75 px.</dimen>
    <dimen name="m4">5.0 px.</dimen>
    <dimen name="m5">6.25 px.</dimen>
    <dimen name="m6">7.5 px.</dimen>
    <dimen name="m7">8.75 px.</dimen>
    <dimen name="m8">10.0 px.</dimen>
    <dimen name="m9">11.25 px.</dimen>
    <dimen name="m10">12.5 px.</dimen>
    //m11 --- m469
    <dimen name="m470">587.5 px.</dimen>
    <dimen name="m471">588.75 px.</dimen>
    <dimen name="m472">590.0 px.</dimen>
    <dimen name="m473">591.25 px.</dimen>
    <dimen name="m474">592.5 px.</dimen>
    <dimen name="m475">593.75 px.</dimen>
    <dimen name="m476">595.0 px.</dimen>
    <dimen name="m477">596.25 px.</dimen>
    <dimen name="m478">597.5 px.</dimen>
    <dimen name="m479">598.75 px.</dimen>
    <dimen name="m480">600.0 px.</dimen>
    </resources>
    
    Copy the code
  2. Here is the Java code to generate the rules (the benchmarks can be changed, of course)

    public class GenerateValueFiles {
    
    	private int baseW;
    	private int baseH;
    
    	private String dirStr = "./res";
    
    	private final static String WTemplate = "<dimen name=\"x{0}\">{1}px</dimen>\n";
    	private final static String HTemplate = "<dimen name=\"y{0}\">{1}px</dimen>\n";
    	private final static String SizeTemplate = "<dimen name=\"m{0}\">{1}px</dimen>\n";
    
    	/** * {0}-HEIGHT */
    	private final static String VALUE_TEMPLATE = "values-{0}x{1}";
    
    	private static final String SUPPORT_DIMESION = "800480; 960540; 1280720; 1920108; 1280400; 1200400; 1280720; 2048."
    			+ "1536;1600,480;1280,480;854,480;480,272;800,432;1200,480;710,440;800,440;1920,480;980,400;1280,408;1280,352;"
    			+ "1280408; 694480; 650480; 1184384; 1024600; 1024720; 1920120; 1076736; 1000700; 480320";
    
    	private String supportStr = SUPPORT_DIMESION;
    
    	public GenerateValueFiles(int baseX, int baseY, String supportStr) {
    		this.baseW = baseX;
    		this.baseH = baseY;
    
    		if (!this.supportStr.contains(baseX + "," + baseY)) {
    			this.supportStr += baseX + "," + baseY + ";";
    		}
    
    		this.supportStr += validateInput(supportStr);
    
    		System.out.println(supportStr);
    
    		File dir = new File(dirStr);
    		if(! dir.exists()) { dir.mkdir(); } System.out.println(dir.getAbsoluteFile()); }/ * * *@paramsupportStr * w,h_... w,h; *@return* /
    	private String validateInput(String supportStr) {
    		StringBuffer sb = new StringBuffer();
    		String[] vals = supportStr.split("_");
    		int w = -1;
    		int h = -1;
    		String[] wh;
    		for (String val : vals) {
    			try {
    				if (val == null || val.trim().length() == 0)
    					continue;
    
    				wh = val.split(",");
    				w = Integer.parseInt(wh[0]);
    				h = Integer.parseInt(wh[1]);
    			} catch (Exception e) {
    				System.out.println("skip invalidate params : w,h = " + val);
    				continue;
    			}
    			sb.append(w + "," + h + ";");
    		}
    
    		return sb.toString();
    	}
    
    	public void generate(a) {
    		String[] vals = supportStr.split(";");
    		for (String val : vals) {
    			String[] wh = val.split(",");
    			generateXmlFile(Integer.parseInt(wh[0]), Integer.parseInt(wh[1])); }}private void generateXmlFile(int w, int h) {
    
    		// 
    		float cellw = w * 1.0 f / baseW;
    		StringBuffer sbForWidth = new StringBuffer();
    		sbForWidth.append("
            \n");
    		sbForWidth.append("<resources>");
    		System.out.println("width : " + w + "," + baseW + "," + cellw );
    		for (int i = 1; i < baseW; i++) {
    			sbForWidth.append(WTemplate.replace("{0}", i + "").replace("{1}", change(cellw * i) + ""));
    		}
    		sbForWidth.append(WTemplate.replace("{0}", baseW + "").replace("{1}", w + ""));
    		sbForWidth.append("</resources>");
    		// 
    		float cellh = h * 1.0 f / baseH;
    		StringBuffer sbForHeight = new StringBuffer();
    		sbForHeight.append("
            \n");
    		sbForHeight.append("<resources>");
    		System.out.println("height : " + h + "," + baseH + "," + cellh);
    		for (int i = 1; i < baseH; i++) {
    			sbForHeight.append(HTemplate.replace("{0}", i + "").replace("{1}", change(cellh * i) + ""));
    		}
    		sbForHeight.append(HTemplate.replace("{0}", baseH + "").replace("{1}", h + ""));
    		sbForHeight.append("</resources>");
    
    		// 
    		float minSize = (cellw < cellh) ? cellw : cellh;
    		StringBuffer sbMinSize = new StringBuffer();
    		sbMinSize.append("
            \n");
    		sbMinSize.append("<resources>");
    		System.out.println("height : " + minSize + "," + cellw + "," + cellh);
    		
    		if(h<baseH){
    			for (int i = 1; i < baseH; i++) {
    				sbMinSize.append(SizeTemplate.replace("{0}", i + "").replace("{1}", change(1.0 f * i) + "")); }}else{
    			for (int i = 1; i < baseH; i++) {
    				sbMinSize.append(SizeTemplate.replace("{0}", i + "").replace("{1}", change(minSize * i) + ""));
    			}
    		}
    		sbMinSize.append(SizeTemplate.replace("{0}", ((cellw < cellh) ? baseW:baseH) +"").replace("{1}", ((cellw < cellh) ? w:h) +""));
    		sbMinSize.append("</resources>");
    		
    		
    
    		File fileDir = new File(dirStr + File.separator + VALUE_TEMPLATE.replace("{0}", w + "")//
    				.replace("{1}", h + ""));
    		fileDir.mkdir();
    
    		File layxFile = new File(fileDir.getAbsolutePath(), "lay_x.xml");
    		File layyFile = new File(fileDir.getAbsolutePath(), "lay_y.xml");
    		File laysizeFile = new File(fileDir.getAbsolutePath(), "lay_size.xml");
    		try {
    			PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));
    			pw.print(sbForWidth.toString());
    			pw.close();
    			pw = new PrintWriter(new FileOutputStream(layyFile));
    			pw.print(sbForHeight.toString());
    			pw.close();
    			pw = new PrintWriter(new FileOutputStream(laysizeFile));
    			pw.print(sbMinSize.toString());
    			pw.close();
    		} catch(FileNotFoundException e) { e.printStackTrace(); }}public static float change(float a) {
    		int temp = (int) (a * 100);
    		return temp / 100f;
    	}
    
    	public static void main(String[] args) {
    		int baseW = 800;
    		int baseH = 480;
    		String addition = "";
    		try {
    			if (args.length >= 3) {
    				baseW = Integer.parseInt(args[0]);
    				baseH = Integer.parseInt(args[1]);
    				addition = args[2];
    			} else if (args.length >= 2) {
    				baseW = Integer.parseInt(args[0]);
    				baseH = Integer.parseInt(args[1]);
    			} else if (args.length >= 1) {
    				addition = args[0]; }}catch (NumberFormatException e) {
    
    			System.err.println("right input params : java -jar xxx.jar width height w,h_w,h_... _w,h;");
    			e.printStackTrace();
    			System.exit(-1);
    		}
    
    		newGenerateValueFiles(baseW, baseH, addition).generate(); }}Copy the code
  3. We also need to dynamically calculate the number of columns that the GridView needs to fill, keep the position fixed instead of centered when the maximum number of columns can be filled is less than the maximum number of columns, and keep the left and right spacing equal when the maximum number of columns can be filled

    mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout(a) {
                    mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    int measuredWidth = mRecyclerView.getMeasuredWidth();// The total size of the control
                    int startSize = getResources().getDimensionPixelOffset(R.dimen.m160);// The width of an Item
                    int count = 1;// Maximum number of columns
                    int spanSize = 0;// The remaining width
                    for (int i = 0; i < 10; i++) {
                        startSize += getResources().getDimensionPixelOffset(R.dimen.m24);// The spacing between items
                        startSize += getResources().getDimensionPixelOffset(R.dimen.m160);
                        if (startSize > measuredWidth) {
                            break;
                        }
                        spanSize = measuredWidth - startSize;
                        count++;
                    }
                    if (BuildConfig.DEBUG) {
                        Log.d("telenewbie::", getClass().getSimpleName() + ",onGlobalLayout:" + count + "," + startSize + "," + measuredWidth + "," + spanSize);
                    }
                    mRecyclerView.setPadding(spanSize / 2.0, spanSize / 2.0);
                    GridLayoutManager gridLayoutManager = newGridLayoutManager(getContext(), count); gridLayoutManager.setOrientation(GridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(gridLayoutManager); }});Copy the code

Let’s call it a day