Android App 개발 에서 View 와 ViewGroup 의 인 스 턴 스 튜 토리 얼 을 사용자 정의 합 니 다.
Android 의 모든 컨트롤 은 View 또는 View 의 하위 클래스 입 니 다.사실은 화면의 사각형 영역 을 표시 합 니 다.Rect 로 표시 합 니 다.left,top 은 View 가 parent View 의 출발점 에 비해 width,height 는 View 자신의 너비 와 높이 를 표시 합 니 다.이 4 개의 필드 를 통 해 View 가 화면 에 있 는 위 치 를 확인 할 수 있 습 니 다.위 치 를 확인 하면 View 의 내용 을 그 릴 수 있 습 니 다.
그리 기 프로 세 스 보기
View 의 그리 기 는 다음 세 가지 과정 으로 나 눌 수 있 습 니 다.
Measure
View 는 먼저 측정 을 해서 자신 이 얼마나 큰 면적 을 차지 해 야 하 는 지 계산한다.View 의 Measure 과정 은 우리 에 게 인터페이스 onMeasure 를 노출 시 켰 다.방법의 정 의 는 이렇다.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
     getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
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;
}
Layout
Layout 과정 은 View 류 에 대해 매우 간단 합 니 다.마찬가지 로 View 는 우리 에 게 onLayout 방법 을 노출 시 켰 습 니 다.
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
Draw
Draw 과정 은 canvas 에 우리 가 필요 로 하 는 View 스타일 을 그 리 는 것 입 니 다.마찬가지 로 View 가 저희 에 게 onDraw 방법 을 노출 시 켰 습 니 다.
protected void onDraw(Canvas canvas) {
}
View 에는 세 가지 중요 한 방법 이 있 습 니 다.
requestLayout
View 에서 layot 프로 세 스 를 다시 호출 합 니 다.
invalidate
View draw 프로 세 스 다시 호출
forceLayout
표지 View 는 다음 에 다시 그립 니 다.layot 과정 을 다시 호출 해 야 합 니 다.
사용자 정의 속성
전체 View 의 그리 기 프로 세 스 를 소 개 했 습 니 다.그리고 중요 한 지식 이 있 습 니 다.컨트롤 속성 을 사용자 정의 합 니 다.View 는 기본 적 인 속성 이 있다 는 것 을 알 고 있 습 니 다.예 를 들 어 layotwidth,layout_height,background 등 우 리 는 자신의 속성 을 정의 해 야 한다.그러면 구체 적 으로 이렇게 할 수 있다.
1.values 폴 더 에서 attrs.xml 를 엽 니 다.사실 이 파일 의 이름 은 임의의 것 일 수 있 습 니 다.여기에 쓰 면 더욱 규범 적 이 고 안에 있 는 것 이 모두 view 의 속성 임 을 표시 합 니 다.
2.아래 의 인 스 턴 스 는 2 개의 길이,하나의 색상 값 의 속성 을 사용 하기 때문에 3 개의 속성 을 만 듭 니 다.
<declare-styleable name="rainbowbar">
 <attr name="rainbowbar_hspace" format="dimension"></attr>
 <attr name="rainbowbar_vspace" format="dimension"></attr>
 <attr name="rainbowbar_color" format="color"></attr>
</declare-styleable>
비교적 간단 한 Google 무지개 진행 표시 줄 을 구현 합 니 다.
간단하게 보기 위해 서 여 기 는 한 가지 색깔 만 사용 하고 여러 가지 색깔 은 여러분 께 남 겨 드 리 겠 습 니 다.우 리 는 바로 코드 를 올 립 니 다.
 
 
public class RainbowBar extends View {
 //progress bar color
 int barColor = Color.parseColor("#1E88E5");
 //every bar segment width
 int hSpace = Utils.dpToPx(80, getResources());
 //every bar segment height
 int vSpace = Utils.dpToPx(4, getResources());
 //space among bars
 int space = Utils.dpToPx(10, getResources());
 float startX = 0;
 float delta = 10f;
 Paint mPaint;
 public RainbowBar(Context context) {
  super(context);
 }
 public RainbowBar(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }
 public RainbowBar(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  //read custom attrs
  TypedArray t = context.obtainStyledAttributes(attrs,
      R.styleable.rainbowbar, 0, 0);
  hSpace = t.getDimensionPixelSize(R.styleable.rainbowbar_rainbowbar_hspace, hSpace);
  vSpace = t.getDimensionPixelOffset(R.styleable.rainbowbar_rainbowbar_vspace, vSpace);
  barColor = t.getColor(R.styleable.rainbowbar_rainbowbar_color, barColor);
  t.recycle();  // we should always recycle after used
  mPaint = new Paint();
  mPaint.setAntiAlias(true);
  mPaint.setColor(barColor);
  mPaint.setStrokeWidth(vSpace);
 }
 .......
}
첫 번 째 방법 은 일반적으로 우리 가 이렇게 사용 할 때 호출 됩 니 다.View view=new View(context);
두 번 째 방법 은 xml 레이아웃 파일 에서 View 를 사용 할 때 inflate 레이아웃 에서 호출 됩 니 다.
세 번 째 방법 은 두 번 째 방법 과 유사 하지만 style 속성 설정 을 증가 합 니 다.이때 inflater 레이아웃 은 세 번 째 구조 방법 을 사용 합 니 다.
위 에서 약간 곤 혹 스 러 워 하 실 수 있 는 것 은 제 가 사용자 정의 속성 hspace,vspace,barcolor 코드 를 세 번 째 구조 방법 에 초기 화 했 지만 Rainbow Bar 는 선형 구조 에 style 속성()을 추가 하지 않 았 습 니 다.그러면 우리 위의 해석 에 따 르 면 inflate 구 조 를 할 때 invoke 두 번 째 구조 방법 이 있 을 것 입 니 다.그러나 우 리 는 두 번 째 구조 방법 에서 세 번 째 구조 방법 인 this(context,attrs,0)를 호출 했다.그래서 세 번 째 구조 방법 에서 사용자 정의 속성 을 읽 는 데 문제 가 없습니다.이것 은 작은 디 테 일 입 니 다.코드 가 불필요 하지 않도록-...
Draw
measrue 와 layot 과정 에 관심 을 가지 지 않 고 onDraw 방법 을 직접 다시 쓰 면 되 기 때문이다.
//draw be invoke numbers.
int index = 0;
@Override
protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  //get screen width
  float sw = this.getMeasuredWidth();
  if (startX >= sw + (hSpace + space) - (sw % (hSpace + space))) {
    startX = 0;
  } else {
    startX += delta;
  }
  float start = startX;
  // draw latter parse
  while (start < sw) {
    canvas.drawLine(start, 5, start + hSpace, 5, mPaint);
    start += (hSpace + space);
  }
  start = startX - space - hSpace;
  // draw front parse
  while (start >= -hSpace) {
    canvas.drawLine(start, 5, start + hSpace, 5, mPaint);
    start -= (hSpace + space);
  }
  if (index >= 700000) {
    index = 0;
  }
  invalidate();
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout   xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_marginTop="40dp"
android:orientation="vertical" >
<com.sw.demo.widget.RainbowBar 
    android:layout_width="match_parent"
  android:layout_height="wrap_content"
  app:rainbowbar_color="@android:color/holo_blue_bright"
  app:rainbowbar_hspace="80dp"
  app:rainbowbar_vspace="10dp"
  ></com.sw.demo.widget.RainbowBar>
</LinearLayout>
 
 사용자 정의 ViewGroup
ViewGroup
우 리 는 View Group 이 View 의 용기 류 라 는 것 을 알 고 있 습 니 다.우리 가 자주 사용 하 는 LinearLayout,RelativeLayout 등 은 모두 View Group 의 하위 클래스 입 니 다.View Group 은 하위 View 가 많 기 때문에 전체 그리 기 과정 은 View 에 비해 복잡 하지만 세 가지 절차 measure,layot,draw 입 니 다.우 리 는 한 번 설명 합 니 다.
Measure
Measure 프로 세 스 는 ViewGroup 의 크기 를 측정 합 니 다.만약 layotwidht 와 layotheight 는 matchparent 또는 구체 적 인 xxxdp 는 간단 합 니 다.set Measured Dimension()방법 을 직접 호출 하여 View Group 의 너비 와 높이 를 설정 하면 됩 니 다.wrap 이 라면.content 는 비교적 번 거 롭 습 니 다.우 리 는 모든 하위 View 를 옮 겨 다 닌 다음 에 모든 하위 View 를 측정 한 다음 에 하위 View 의 배열 규칙 에 따라 최종 View Group 의 크기 를 계산 해 야 합 니 다.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 int childCount = this.getChildCount();
 for (int i = 0; i < childCount; i++) {
   View child = this.getChildAt(i);
   this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
   int cw = child.getMeasuredWidth();
   // int ch = child.getMeasuredHeight();
 }
}
Layout
위 View 의 사용자 정의 에서 우 리 는 layot 과정 은 사실 하위 View 의 위 치 를 배열 하 는 것 이 라 고 약간 언급 했다.onLayout 방법 은 나 에 게 우리 가 원 하 는 규칙 에 따라 의 자 View 를 배열 할 기 회 를 주 었 다.
@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
 int childCount = this.getChildCount();
 for (int i = 0; i < childCount; i++) {
   View child = this.getChildAt(i);
   LayoutParams lParams = (LayoutParams) child.getLayoutParams();
   child.layout(lParams.left, lParams.top, lParams.left + childWidth,
       lParams.top + childHeight);
 }
}
Draw
ViewGroup 은 draw 단계 에 있 습 니 다.사실은 하위 클래스 의 배열 순서에 따라 하위 클래스 의 onDraw 방법 을 호출 하 는 것 입 니 다.우 리 는 View 의 용기 일 뿐 그 자체 가 draw 추가 적 인 수식 이 필요 하지 않 기 때문에 onDraw 방법 에 서 는 ViewGroup 의 onDraw 기본 구현 방법 만 사용 하면 됩 니 다.
LayoutParams
ViewGroup 에 중요 한 지식 이 하나 더 있 습 니 다.LayoutParams 는 하위 View 가 ViewGroup 에 가입 할 때의 매개 변수 정 보 를 저장 합 니 다.ViewGroup 클래스 를 계승 할 때 보통 새로운 LayoutParams 클래스 를 새로 만들어 야 합 니 다.SDK 에서 우리 가 잘 아 는 LinearLayout.LayoutParams,RelativeLayout.LayoutParams 클래스 등 과 같이 이렇게 할 수 있 습 니 다.정 의 된 ViewGroup 하위 클래스 에 LayoutParams 클래스 계승 과 ViewGroup.LayoutParams 를 새로 만 듭 니 다.
public static class LayoutParams extends ViewGroup.LayoutParams {
 public int left = 0;
 public int top = 0;
 public LayoutParams(Context arg0, AttributeSet arg1) {
   super(arg0, arg1);
 }
 public LayoutParams(int arg0, int arg1) {
   super(arg0, arg1);
 }
 public LayoutParams(android.view.ViewGroup.LayoutParams arg0) {
   super(arg0);
 }
}
@Override
public android.view.ViewGroup.LayoutParams generateLayoutParams(
   AttributeSet attrs) {
 return new NinePhotoView.LayoutParams(getContext(), attrs);
}
@Override
protected android.view.ViewGroup.LayoutParams generateDefaultLayoutParams() {
 return new LayoutParams(LayoutParams.WRAP_CONTENT,
     LayoutParams.WRAP_CONTENT);
}
@Override
protected android.view.ViewGroup.LayoutParams generateLayoutParams(
   android.view.ViewGroup.LayoutParams p) {
 return new LayoutParams(p);
}
@Override
protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams p) {
 return p instanceof NinePhotoView.LayoutParams;
}
우 리 는 오늘 위 챗 모멘트 에 그림 을 보 낼 컨트롤 을 만 들 고+번 그림 을 누 르 면 그림 을 계속 추가 할 수 있 습 니 다.최대 9 장 까지 추가 할 수 있 습 니 다.그러면 위 챗 은 4 개 1 열 입 니 다.여 기 는 3 개 1 열 입 니 다.보통 3 개 1 열 이기 때문에 디 테 일 에 신경 쓰 지 마 세 요.
 
 
public class NinePhotoView extends ViewGroup {
public static final int MAX_PHOTO_NUMBER = 9;
private int[] constImageIds = { R.drawable.girl_0, R.drawable.girl_1,
   R.drawable.girl_2, R.drawable.girl_3, R.drawable.girl_4,
   R.drawable.girl_5, R.drawable.girl_6, R.drawable.girl_7,
   R.drawable.girl_8 };
// horizontal space among children views
int hSpace = Utils.dpToPx(10, getResources());
// vertical space among children views
int vSpace = Utils.dpToPx(10, getResources());
// every child view width and height.
int childWidth = 0;
int childHeight = 0;
// store images res id
ArrayList<integer> mImageResArrayList = new ArrayList<integer>(9);
private View addPhotoView;
public NinePhotoView(Context context) {
 super(context);
}
public NinePhotoView(Context context, AttributeSet attrs) {
 this(context, attrs, 0);
}
public NinePhotoView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 TypedArray t = context.obtainStyledAttributes(attrs,
     R.styleable.NinePhotoView, 0, 0);
 hSpace = t.getDimensionPixelSize(
     R.styleable.NinePhotoView_ninephoto_hspace, hSpace);
 vSpace = t.getDimensionPixelSize(
     R.styleable.NinePhotoView_ninephoto_vspace, vSpace);
 t.recycle();
 addPhotoView = new View(context);
 addView(addPhotoView);
 mImageResArrayList.add(new integer());
}
Measure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 int rw = MeasureSpec.getSize(widthMeasureSpec);
 int rh = MeasureSpec.getSize(heightMeasureSpec);
 childWidth = (rw - 2 * hSpace) / 3;
 childHeight = childWidth;
 int childCount = this.getChildCount();
 for (int i = 0; i < childCount; i++) {
   View child = this.getChildAt(i);
   //this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
   LayoutParams lParams = (LayoutParams) child.getLayoutParams();
   lParams.left = (i % 3) * (childWidth + hSpace);
   lParams.top = (i / 3) * (childWidth + vSpace);
 }
 int vw = rw;
 int vh = rh;
 if (childCount < 3) {
   vw = childCount * (childWidth + hSpace);
 }
 vh = ((childCount + 3) / 3) * (childWidth + vSpace);
 setMeasuredDimension(vw, vh);
}
Layout
@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
 int childCount = this.getChildCount();
 for (int i = 0; i < childCount; i++) {
   View child = this.getChildAt(i);
   LayoutParams lParams = (LayoutParams) child.getLayoutParams();
   child.layout(lParams.left, lParams.top, lParams.left + childWidth,
       lParams.top + childHeight);
   if (i == mImageResArrayList.size() - 1 && mImageResArrayList.size() != MAX_PHOTO_NUMBER) {
     child.setBackgroundResource(R.drawable.add_photo);
     child.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View arg0) {
         addPhotoBtnClick();
       }
     });
   }else {
     child.setBackgroundResource(constImageIds[i]);
     child.setOnClickListener(null);
   }
 }
}
public void addPhoto() {
 if (mImageResArrayList.size() < MAX_PHOTO_NUMBER) {
   View newChild = new View(getContext());
   addView(newChild);
   mImageResArrayList.add(new integer());
   requestLayout();
   invalidate();
 }
}
public void addPhotoBtnClick() {
 final CharSequence[] items = { "Take Photo", "Photo from gallery" };
 AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
 builder.setItems(items, new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {
     addPhoto();
   }
 });
 builder.show();
}
Draw
다시 쓸 필요 가 없습니다.ViewGroup 기본 값 으로 실행 하면 됩 니 다.
레이아웃 파일 첨부
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="40dp"
android:orientation="vertical" >
<com.sw.demo.widget.NinePhotoView
  android:id="@+id/photoview"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  app:ninephoto_hspace="10dp"
  app:ninephoto_vspace="10dp"
  app:rainbowbar_color="@android:color/holo_blue_bright" >
</com.sw.demo.widget.NinePhotoView>
</LinearLayout>

이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.