編輯:關於Android編程
如下圖:
之前寫過一個可以雙向滑動的和這個view的類似,那個滑動的view處理的onTouch事件,以及判斷了我們應該滑動哪個小球,有興趣的可以看下之前的連接
雙向滑動的SeekBarhttp://blog.csdn.net/givemeacondom/article/details/52397589
這個就比較簡單了,都是靜態的繪制,唯一的交互就是UI中的簽到按鈕,點擊一次通知自定義view繪制;
上面就是實現的思路,我們一步步看下代碼,最後會奉上源代碼的下載鏈接;
private void initAttrs(Context context, AttributeSet attrs, int defStyleAttr) { TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SignInView, defStyleAttr, R.style.def_sign_in_style); int indexCount = typedArray.getIndexCount(); for (int i = 0; i < indexCount; i++) { int attr = typedArray.getIndex(i); switch (attr) { case R.styleable.SignInView_sign_in_bg_clor: signInBgColor = typedArray.getColor(attr, Color.BLACK); break; case R.styleable.SignInView_sign_in_pb_clor: signInPbColor = typedArray.getColor(attr, Color.BLACK); break; case R.styleable.SignInView_sign_in_check_clor: signInCheckColor = typedArray.getColor(attr, Color.BLACK); break; case R.styleable.SignInView_sign_in_text_clor: singInTextColor = typedArray.getColor(attr, Color.BLACK); break; case R.styleable.SignInView_sign_in_text_size: singInTextSize = typedArray.getDimensionPixelSize(attr, 0); break; } } typedArray.recycle(); }
根據需求我們的這個view默認充滿屏幕,所以只需要處理height的測量模式即可
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Log.e("--TAG---", "onMeasure--->>"); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int measureHeight; if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) { measureHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEF_HEIGHT, getResources().getDisplayMetrics()); heightMeasureSpec = MeasureSpec.makeMeasureSpec(measureHeight, MeasureSpec.EXACTLY); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); Log.e("--TAG---", "onSizeChanged--->>"); viewPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEF_PADDING, getResources().getDisplayMetrics()); int textMarginTop = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, TEXT_MARGIN_TOP, getResources().getDisplayMetrics()); // 在回調中獲取view的寬度和高度,並計算小球的半徑 viewWidth = w; viewHeight = h; signInBallRadio = (int) (viewHeight * SIGN_IN_BALL_SCALE / 2); signRectHeight = (int) (signInBallRadio * SIGN_BG_RECT_SCALE); // 矩形的背景區域, signInBgRectF = new RectF(viewPadding + signInBallRadio, viewHeight * SECTION_SCALE - signInBallRadio - signRectHeight, viewWidth - viewPadding - signInBallRadio, viewHeight * SECTION_SCALE - signInBallRadio); circleY = (int) (signInBgRectF.top + signRectHeight / 2); descY = (int) (viewHeight * SECTION_SCALE) + signInBallRadio + textMarginTop; // 核心代碼就一個方法 calculateCirclePoints(viewData); progressRectF = signInPbRectFs.get(0); }
private void calculateCirclePoints(ListviewData) { //獲取數據源中分開的份數,比如一周7天,中間的小段是不是有6段,所以是size-1 if (null != viewData) { int intervalSize = viewData.size() - 1; // 計算一份的長度,view的寬度減去兩邊的padding,以及總共小球的半徑的綜合然後除以,一共多少份 int onePiece = (viewWidth - 2 * viewPadding - signInBallRadio * 2 * viewData.size()) / intervalSize; // 計算每個小球的坐標,x 和y, for (int i = 0; i < viewData.size(); i++) { // 這裡其實很簡單,就是一個小小的規律,當前的小球坐標 // 第一個小球 0個onePiece + 1個小球半徑 // 第二個小球 1個onePiece + 3個小球半徑 // 第三個小球 2個onePiece + 5個小球半徑 // ####........求第N個的距離......?是不是一個送分題 // 第N個的公式: i * onePiece + ((i + 1) * 2 - 1)*R Point circleP = new Point(viewPadding + i * onePiece + ((i + 1) * 2 - 1) * signInBallRadio, circleY); Point descP = new Point((int) (viewPadding + i * onePiece + ((i + 1) * 2 - 1) * signInBallRadio - signInTextPaint.measureText(viewData.get(i)) / 2), descY); // 計算對勾的路徑 Path signInPath = new Path(); signInPath.moveTo(circleP.x - signInBallRadio / 2, circleP.y); signInPath.lineTo(circleP.x, circleP.y + signInBallRadio / 2); signInPath.lineTo(circleP.x + signInBallRadio / 2, circleP.y - signInBallRadio + signInBallRadio / 2); RectF signInPbRectF = new RectF(viewPadding + signInBallRadio, viewHeight * SECTION_SCALE - signInBallRadio - signRectHeight, circleP.x, viewHeight * SECTION_SCALE - signInBallRadio); // 保存計算的坐標; signInPaths.add(signInPath); circlePoints.add(circleP); descPoints.add(descP); signInPbRectFs.add(signInPbRectF); } } }
/** * 屬性動畫的形式繪制進度條,暫時未啟用,效果預覽可以的 * * @param canvas */ private void drawSignInPbRectWithAnim(final Canvas canvas) { if (isNeedReturn()) { return; } final RectF targetRectF = signInPbRectFs.get(currentSignTag); RectF beforeRectF; if (currentSignTag >= 1) { beforeRectF = signInPbRectFs.get(currentSignTag - 1); progressRectF.left = beforeRectF.left; } progressRectF.right = targetRectF.right * persent; canvas.drawRect(progressRectF, signInPbPaint); } // 繪制簽到進度的方法,如果想看動畫的可以自己把ondraw中的方法替換成上面的即可 private void drawSignInPbRectNoAnim(final Canvas canvas) { if (isNeedReturn()) { return; } canvas.drawRect(signInPbRectFs.get(currentSignTag), signInPbPaint); } private boolean isNeedReturn() { return currentSignTag < 0 || currentSignTag >= viewData.size(); } // 繪制選中的簽到狀態下的圓圈以及白色的對勾路徑 private void drawSingInCheckCircle(Canvas canvas) { if (isNeedReturn()) { return; } for (int i = -1; i < currentSignTag; i++) { Point p = circlePoints.get(i + 1); Path path = signInPaths.get(i + 1); canvas.drawCircle(p.x, p.y, signInBallRadio, signInPbPaint); canvas.drawPath(path, signInCheckPaint); } } // 繪制簽到信息描述 private void drawSignDesc(Canvas canvas) { for (int i = 0; i < viewData.size(); i++) { Point p = descPoints.get(i); canvas.drawText(viewData.get(i), p.x, p.y, signInTextPaint); } } // 繪制白色的未簽到背景 private void drawSignInNormalCircle(Canvas canvas) { for (Point p : circlePoints) { canvas.drawCircle(p.x, p.y, signInBallRadio, signInBgPaint); } } private void drawSignInBgRect(Canvas canvas) { canvas.drawRect(signInBgRectF, signInBgPaint); }
java虛擬機基本結構:JVM是一個內存中的虛擬機,那它的存儲就是內存了,我們寫的所有類、常量、變量、方法都在內存中,因此明白java虛擬機的內存分配非常重要,本部分主要
DatePicker實戰效果圖:依賴導入compile 'cn.aigestudio.datepicker:DatePicker:2.2.0'DatePi
前言相信大家都知道,當使用AppCompatEditText(Edit Text)時,默認的下劃線是跟隨系統的#FF4081的顏色值的,通過改變這個值可以改變所有的顏色樣
在程序開發中,為了讓程序表現的更快更流暢,我們會使用多線程來提升應用的並發性能。但多線程並發代碼是一個棘手的問題,線程的生命周期處理不好就會造成內存洩漏。 new