圓角點選效果+適配
- 圓角
- 按下的效果
- 同一個佈局,兩行效果和一行效果
- 最外還有1px的線條
可能本人才疏學淺,最終是在別人的幫助才完成這個效果的。
一共需要注意的幾個點:
- 圓角用的是自定義控制元件
- 自定義控制元件可以控制最外層是否有線條
- 點選效果
- 單行變雙行,雙行變單行怎麼做到
- 如何彈出介面在window上面,
正式開始
-
圓角的自定義控制元件
-
複製可用
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() { 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) { // 建立圖層A,繪製圓角矩陣 canvas.saveLayer(roundRect, zonePaint, Canvas.ALL_SAVE_FLAG); canvas.drawRoundRect(roundRect, rect_adius, rect_adius, zonePaint); // 建立圖層B,使用xfermode將圖層的形狀變成圓角矩陣 canvas.saveLayer(roundRect, maskPaint, Canvas.ALL_SAVE_FLAG); // 清空圖層的顏色 canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); super.draw(canvas); // 將圖層B繪製到canvas上 if (borderPaint != null) { canvas.drawRoundRect(roundRect, rect_adius, rect_adius, borderPaint); } canvas.restore(); } public void setCorner(float adius) { rect_adius = adius; invalidate(); } } 複製程式碼
-
複製可用
-
控制線條
- 在attrs.xml中加上這個來進行控制圓角大小以及邊框大小
<declare-styleable name="CornerLinearLayout"> <attr name="cornerRadius" /> <attr name="border_width" /> </declare-styleable> 複製程式碼
-
點選效果
-
一開始真的沒有想到這個效果要怎麼辦,後來大神說:透明就可以了。於是就變成這樣了。
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> 複製程式碼
-
之後只要對點選的範圍設定背景色就好了eg:
<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="取消" /> 複製程式碼
-
-
單行變雙行
-
我用的方式是隱藏+改變元件大小
<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="取消" /> </com.txznet.music.widget.CornerLinearLayout> 複製程式碼
-
然後變成一行只需要將最外層的控制元件大小改為一行的大小,比方說我這裡設計師給到的單行的高度是:72【800x480的裝置】
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); } 複製程式碼
-
-
新增到主window到螢幕上
-
本人4.4的裝置,所以許可權方面沒有考慮,因為框架預設申請5.0所有的許可權
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); 複製程式碼
-
簡單適配方案
-
也許你注意到了程式碼裡面或者xml裡面設定長寬都是採用
@dimen/m120
這樣的方式來進行的,因為這裡採用的不是今日頭條的適配方案,而是再早之前的等比例縮放的方式即按照基準(800x480)來進行換算不同螢幕的解析度底下的值,比方說:在800x480上面的寬 1px 相當於1024x600上面的幾個畫素,高1px相當於1024x600上面的多少個畫素?經過換算需要顯示的大小為:h = 1px * 600/480 = 1.25pxw = 1px * 1024/800 = 1.28px
也許你會有疑問【可能經歷過才知道,在車載環境裡面各種奇葩的裝置,防不勝防,有的長度很長,但高度很短,有的高度很高,但是長度很短】,那不是我一個正方形的圖片會被拉成奇怪的長條狀。正式因為考慮到這個【或者說遇到這個bug】,所以有了最小比例之說,比方說:上面的比例1.25 和1.28 我們就用最小的解析度來進行換算生成一套最小的解析度來進行設定,比方說:1.25和1.28我們就採用1.25的規則,於是就有了這樣的一個檔案:【當然這個檔案可以通過java來進行自動生成,寫好規則就好了】
<?xml version="1.0" encoding="utf-8"?> <resources><dimen name="m1">1.25px</dimen> <dimen name="m2">2.5px</dimen> <dimen name="m3">3.75px</dimen> <dimen name="m4">5.0px</dimen> <dimen name="m5">6.25px</dimen> <dimen name="m6">7.5px</dimen> <dimen name="m7">8.75px</dimen> <dimen name="m8">10.0px</dimen> <dimen name="m9">11.25px</dimen> <dimen name="m10">12.5px</dimen> //m11 --- m469 <dimen name="m470">587.5px</dimen> <dimen name="m471">588.75px</dimen> <dimen name="m472">590.0px</dimen> <dimen name="m473">591.25px</dimen> <dimen name="m474">592.5px</dimen> <dimen name="m475">593.75px</dimen> <dimen name="m476">595.0px</dimen> <dimen name="m477">596.25px</dimen> <dimen name="m478">597.5px</dimen> <dimen name="m479">598.75px</dimen> <dimen name="m480">600.0px</dimen> </resources> 複製程式碼
-
這裡附上生成規則的java程式碼【當然基準是可以修改的】
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 = "800,480;960,540;1280,720;1920,1080;1280,400;1200,400;1280,720;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;" + "1280,408;694,480;650,480;1184,384;1024,600;1024,720;1920,1200;1076,736;1000,700;480,320"; 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()); } /** * @param supportStr *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() { 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.0f / baseW; StringBuffer sbForWidth = new StringBuffer(); sbForWidth.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\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.0f / baseH; StringBuffer sbForHeight = new StringBuffer(); sbForHeight.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\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("<?xml version=\"1.0\" encoding=\"utf-8\"?>\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.0f * 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); } new GenerateValueFiles(baseW, baseH, addition).generate(); } } 複製程式碼
-
這裡還要講下,動態計算gridview需要填充的列數,在可填充的最大列數上呈現不足最大列數個數的時候保持位置固定而非居中,在呈現最大列數的時候能夠保持左右間距相等
mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); int measuredWidth = mRecyclerView.getMeasuredWidth();//控制元件總共擁有的空間大小 int startSize = getResources().getDimensionPixelOffset(R.dimen.m160);//一個Item的寬度 int count = 1;//最大的列數 int spanSize = 0;//剩餘的寬度 for (int i = 0; i < 10; i++) { startSize += getResources().getDimensionPixelOffset(R.dimen.m24);//item與item之間的間距 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 = new GridLayoutManager(getContext(), count); gridLayoutManager.setOrientation(GridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(gridLayoutManager); } }); 複製程式碼