Android 사용자 정의 ViewGroup 의 CustomGridLayout(1)

이전에 사용자 정의 view 에 관 한 두 편의 글 을 썼 는데,이 글 은 사용자 정의 View Group 의 실현 을 강의 합 니 다.
우 리 는 View Group 이 바로 View 의 용기 류 라 는 것 을 알 고 있 습 니 다.우리 가 자주 사용 하 는 LinearLayout,Relative Layout 등 은 모두 View Group 의 하위 클래스 입 니 다.또한 레이아웃 xml 을 쓸 때 용기(layot 로 시작 하 는 속성 은 모두 용기 에 알려 주기 위 한 것),우리 의 너비(layotwidth),높이(layoutheight),정렬 방식(layotgravity)등;따라서 View Group 의 기능 은 childView 에 건의 하 는 너비 와 높이 와 측정 모델 을 계산 하 는 것 이다.childView 의 위 치 를 결정 합 니 다.왜 직접 확인 하 는 것 이 아니 라 너비 와 높이 만 권장 하 는 지 잊 지 마 세 요.childView 너비 와 높이 는 wrap 로 설정 할 수 있 습 니 다.content,이렇게 해야만 childView 만 이 자신의 너비 와 높이 를 계산 할 수 있 습 니 다.
View 는 ViewGroup 에서 들 어 오 는 측정 값 과 패턴 에 따라 자신의 너비 와 높이 를 확인 한 다음 에 onDraw 에서 자신 에 대한 그리 기 를 완성 합 니 다.ViewGroup 은 View 에 view 의 측정 값 과 모드(onMeasure 에서 완료)를 전달 해 야 하 며,이 ViewGroup 의 부모 레이아웃 에 대해 서도 onMeasure 에서 자신의 너비 와 높이 를 확인 해 야 합 니 다.또한,onLayout 에서 childView 의 위 치 를 지정 해 야 합 니 다.
ViewGroup 에는 하위 View 가 많 기 때문에 전체 그리 기 과정 은 View 에 비해 복잡 하지만 세 가지 절차 measure,layot,draw 에 따라 차례대로 설명 합 니 다.
본 고 는 GridView 와 유사 한 격자 용 기 를 쓰 자.우선 Custom GridView 라 고 부른다.
사용자 정의 속성/속성 값 가 져 오기

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="CustomGridView">
 <attr name="numColumns" format="integer" />
 <attr name="hSpace" format="integer" />
 <attr name="vSpace" format="integer" />
 </declare-styleable>
</resources>


 public CustomGridView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 if (attrs != null) {
  TypedArray a = getContext().obtainStyledAttributes(attrs,
   R.styleable.CustomGridView);
  colums = a.getInteger(R.styleable.CustomGridLayout_numColumns, 3);
  hSpace = a.getInteger(R.styleable.CustomGridLayout_hSpace, 10);
  vSpace = a.getInteger(R.styleable.CustomGridLayout_vSpace, 10);
  a.recycle();
 }
 }

 public MyGridLayout(Context context, AttributeSet attrs) {
 this(context, attrs, 0);
 }

 public MyGridLayout(Context context) {
 this(context, null);
 }
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);
  } 
 }

이제 새로운 LayoutParams 클래스 가 생 겼 습 니 다.사용자 정의 ViewGroup 은 사용자 정의 LayoutParams 클래스 를 사용 하여 하위 View 를 추가 할 수 있 습 니까?ViewGroup 역시 다음 과 같은 몇 가지 방법 을 제공 합 니 다.사용자 정의 LayoutParams 대상 을 다시 쓰 면 됩 니 다.

 @Override
 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
 return new CustomGridLayout.LayoutParams(getContext(), attrs);
 }

 @Override
 protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
 }

 @Override
 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
 return new LayoutParams(p);
 }

 @Override
 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
 return p instanceof CustomGridLayout.LayoutParams;
 }

measure
onMeasure 에서 두 가지 일 을 해 야 합 니 다.
 •childView 의 측정 값 과 패턴 을 계산 합 니 다.
measureChildren(widthMeasureSpec, heightMeasureSpec);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
child.measure(WidthMeasureSpec, HeightMeasureSpec);
 •ViewGroup 의 너비 와 높이 설정
View Group 의 크기 를 측정 합 니 다.layotwidth 와 layotheight 는 matchparent 또는 구체 적 인 xxxdp 는 간단 합 니 다.set Measured Dimension()방법 을 직접 호출 하여 View Group 의 너비 와 높이 를 설정 하면 됩 니 다.wrap 이 라면.content 는 비교적 번 거 롭 습 니 다.우 리 는 모든 하위 View 를 옮 겨 다 닌 다음 에 모든 하위 View 를 측정 한 다음 에 하위 View 의 배열 규칙 에 따라 최종 View Group 의 크기 를 계산 해 야 합 니 다.
메모:사용자 정의 View 첫 번 째 스 펙 모드 에서 UNSPECIFIED 는 일반적으로 부모 컨트롤 이 AdapterView 이 고 measure 방법 으로 들 어 오 는 모드 라 고 말 한 적 이 있 습 니 다.여기 서 마침 쓰 였 다. 

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
 int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);

 //UNSPECIFIED        AdapterView,  measure       
 final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(sizeWidth, MeasureSpec.UNSPECIFIED);
 final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(sizeHeight, MeasureSpec.UNSPECIFIED);
 measureChildren(childWidthMeasureSpec, childHeightMeasureSpec);

 int childCount = this.getChildCount();
 int line = childCount % colums == 0 ? childCount / colums : (childCount + colums) / colums;

 //    wrap_content ,childWidth childView     ,      
 if (widthMode == MeasureSpec.AT_MOST) {
  for (int i = 0; i < childCount; i++) {
  View child = this.getChildAt(i);
  childWidth = Math.max(childWidth, child.getMeasuredWidth());
  }
 } else if (widthMode == MeasureSpec.EXACTLY) {
  childWidth = (sizeWidth - (colums - 1) * hSpace) / colums;
 }
 //    wrap_content ,childHeight childView     ,      
 if (heightMode == MeasureSpec.AT_MOST) {
  for (int i = 0; i < childCount; i++) {
  View child = this.getChildAt(i);
  childHeight = Math.max(childHeight, child.getMeasuredHeight());
  }
 } else if (heightMode == MeasureSpec.EXACTLY) {
  childHeight = (sizeHeight - (line - 1) * vSpace) / line;
 }

 //     view,              LayoutParams ,   onLayout  
 for (int i = 0; i < childCount; i++) {
  View child = this.getChildAt(i);
  LayoutParams lParams = (LayoutParams) child.getLayoutParams();
  lParams.left = (i % colums) * (childWidth + hSpace);
  lParams.top = (i / colums) * (childHeight + vSpace);
 }
 //    wrap_content ,      viewGroup  
 int wrapWidth;
 int wrapHeight;
 if (childCount < colums) {
  wrapWidth = childCount * childWidth + (childCount - 1) * hSpace;
 } else {
  wrapWidth = colums * childWidth + (colums - 1) * hSpace;
 }
 wrapHeight = line * childHeight + (line - 1) * vSpace;
 setMeasuredDimension(widthMode == MeasureSpec.AT_MOST? wrapWidth:sizeWidth,heightMode == MeasureSpec.AT_MOST? wrapHeight:sizeHeight);
 }

layout
가장 핵심 적 인 것 은 layot 방법 을 호출 하 는 것 입 니 다.우리 measure 단계 에서 얻 은 LayoutParams 의 left 와 top 필드 에 따라 모든 하위 View 에 위 치 를 배열 하 는 것 도 좋 습 니 다.

@Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
 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 기본 구현 방법 만 사용 하면 됩 니 다.다시 쓸 필요 없어.
마지막 으로 사용자 정의 ViewGroup 에서 GridAdatper 인 터 페 이 스 를 정의 하여 외부 에서 ViewGroup 에 어댑터 를 설정 할 수 있 도록 합 니 다.

 public interface GridAdatper {
 View getView(int index);
 int getCount();
 }

 /**       */
 public void setGridAdapter(GridAdatper adapter) {
 this.adapter = adapter;
 //       
 int size = adapter.getCount();
 for (int i = 0; i < size; i++) {
  addView(adapter.getView(i));
 }
 }

또한 사용자 정의 ViewGroup 에서 OnitemClickListener 인 터 페 이 스 를 정의 하여 외부 에서 childView 의 클릭 이 벤트 를 가 져 올 수 있 도록 합 니 다.

public interface OnItemClickListener {
 void onItemClick(View v, int index);
 }

 public void setOnItemClickListener(final OnItemClickListener listener) {
 if (this.adapter == null)
  return;
 for (int i = 0; i < adapter.getCount(); i++) {
  final int index = i;
  View view = getChildAt(i);
  view.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
   listener.onItemClick(v, index);
  }
  });
 }
 }

사용자 정의 CustomView Group 사용 하기
레이아웃 파일:

<?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/com.hx.customgridview"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="#303030"
 android:orientation="vertical" >

 <com.hx.customgridview.CustomGridLayout
 android:id="@+id/gridview"
 android:layout_width="200dp"
 android:layout_height="300dp"
 android:background="#1e1d1d"
 app:hSpace="10"
 app:vSpace="10"
 app:numColumns="3"/>
</LinearLayout>

grid_item:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:gravity="center"
 android:orientation="vertical" >
 <ImageView
 android:id="@+id/iv"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:scaleType="fitXY"/>
</LinearLayout>

 Java 파일:

protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 grid = (CustomGridLayout) findViewById(R.id.gridview);
 grid.setGridAdapter(new GridAdatper() {
  @Override
  public View getView(int index) {
  View view = getLayoutInflater().inflate(R.layout.grid_item, null);
  ImageView iv = (ImageView) view.findViewById(R.id.iv);
  iv.setImageResource(srcs[index]);
  return view;
  }

  @Override
  public int getCount() {
  return srcs.length;
  }
 });
 grid.setOnItemClickListener(new OnItemClickListener() {
  @Override
  public void onItemClick(View v, int index) {
  Toast.makeText(MainActivity.this, "item="+index, Toast.LENGTH_SHORT).show();
  }
 });
 }
}

실행 후 효과 그림 은 다음 과 같 습 니 다:
这里写图片描述
레이아웃 변경:

<com.hx.customgridview.CustomGridLayout
  android:id="@+id/gridview"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="#1e1d1d"
  app:hSpace="10"
  app:vSpace="10"
  app:numColumns="3"/>

这里写图片描述
더욱 바꾼다

<com.hx.customgridview.CustomGridLayout
  android:id="@+id/gridview"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#1e1d1d"
  app:hSpace="10"
  app:vSpace="10"
  app:numColumns="3"/>

这里写图片描述
재 변

<com.hx.customgridview.CustomGridLayout
  android:id="@+id/gridview"
  android:layout_width="wrap_content"  

  android:layout_height="wrap_content"
  android:background="#1e1d1d"
  app:hSpace="10"
  app:vSpace="10"
  app:numColumns="4"/>

这里写图片描述
데모 다운로드 주소:http://xiazai.jb51.net/201609/yuanma/CustomGridLayout(jb51.net).rar
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기