ItemDecoration을 이용하여 divider를 추가할 수도 있고,

각 item view에 padding을 줘서 list를 더 이쁘게 보여줄 수도 있다.


그리고 ItemAnimator를 이용해서, item view가 추가/삭제/이동 할때 animation을 개발자가 원하는대로 바꿀 수 있다.




DividerItemDecoration.java

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

private Drawable parentDivider;
private Drawable subDivider;

public DividerItemDecoration(Context context) {
parentDivider = context.getDrawable(R.drawable.parent_divider);
subDivider = context.getDrawable(R.drawable.sub_divider);
}

// canvas에 decoration을 draw
// item view가 그려지기전에 decoration이 먼저 그려지며, view밑에 나타난다.
@Override
public void onDraw(Canvas c, RecyclerView recyclerView, RecyclerView.State state) {

int left = recyclerView.getPaddingLeft();
int right = recyclerView.getWidth() - recyclerView.getPaddingRight();

int childCount = recyclerView.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = recyclerView.getChildAt(i);
RecyclerView.ViewHolder viewHolder = recyclerView.getChildViewHolder(child);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

int top = child.getBottom() + params.bottomMargin;

if(viewHolder instanceof NewItemAdapter.ParentItemVH){
int bottom = top + parentDivider.getIntrinsicHeight();
parentDivider.setBounds(left, top, right, bottom);
parentDivider.draw(c);
}else{
int bottom = top + subDivider.getIntrinsicHeight();
subDivider.setBounds(left+20, top, right-20, bottom);
subDivider.draw(c);
}
}

}


// outRect를 인자로 보내면, outRect에 값들을 넣어줌. (call by reference)
// outRect의 각 자리는 각 item의 offset을 정해줘야함.
// default는 다 0
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

Log.
i(TAG, "getItemOffsets");

RecyclerView.ViewHolder viewHolder = (parent.getChildViewHolder(view));

if(viewHolder instanceof ItemAdapter.ParentItemVH){
//outRect.set(0, 20, 0, parentDivider.getIntrinsicHeight());
outRect.set(0, 20, 0, 1);
}else{
//outRect.set(20, 1, 20, subDivider.getIntrinsicHeight());
outRect.set(20, 1, 20, 1);
}
}
}

앞 포스팅에서는 안썼으나, 

Expandable Recyclerview를 구현하면서 ViewHolder를 Parent와 Child로 나누어서 개발하였기 때문에,

decoration도 위와 같이 viewHolder의 종류에 따라서 offset과 divider의 종류를 다르게 주었다.

각 offset 값은 각 뷰의 margin left top right bottom의 역할을 한다고 보면 된다.



MyItemAnimator.java

public class MyItemAnimator extends DefaultItemAnimator{

@Override
public boolean animateAdd(RecyclerView.ViewHolder holder) {
return super.animateAdd(holder);
}

@Override
public boolean animateRemove(RecyclerView.ViewHolder holder) {

View view = holder.itemView;

ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 1.0f, 0.0f).setDuration(100);
alphaAnimator.setInterpolator(new DecelerateInterpolator());
alphaAnimator.start();

return true;
}

@Override
public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {

return super.animateMove(holder, fromX, fromY, toX, toY);
}
}

DefaultItemAnimator를 상속받아서 만들었다.

DefaultItemAnimator는 SimpleItemAnimator를 상속받았고,

SimpleItemAnimator는 RecyclerView.ItemAnimator를 상속받았다.


DefaultItemAnimator는 item들의 추가/삭제/이동 이벤트에 대한 기본적인 animation을 제공하는 클래스이다.

RecyclerView는 이를 기본적인 ItemAnimator로 사용한다.


나는 아이템이 삭제되는 애니메이션을 customizing 해보았는데,

ObjectAnimator를 사용하여, 알파값이 1에서 0으로, 

처음엔 느리다가 나중에 빨라지는 decelerate를 interpolator로 주어 애니메이션을 설정하였다.



animation은 언제 적용이 되는가를 살펴보면,

앞에서 adapter에서 data의 변화가 있을 때, notify를 통해서 adapter를 구독하는 observer들에게 알려주고,

observer들이 변화에 맞게 layout을 갱신한다고 하였다.

이 layout을 갱신할 때, 알맞은 animation도 함께 요청하면서 animation이 적용된다.


공부한 내용을 정리한 글

RecyclerView를 구현하는 법 보다는, 내부 동작에 대해 써놓은 글입니다.

구현 코드는 인터넷에 많으니깐ㅎㅎ


정의를 보면, RecyclerView는 커다란 data set을 제한된 window 공간에서 유연하게 표현할 수 있는 뷰이다.

ListView에서 더 유연하게 사용할 수 있도록 확장한 버전이다.


RecyclerView는 300개의 data set이 있고 화면상에는 10개의 data set만 보여진다면,

10개의 data set 공간을 생성한 후, 그 공간을 재활용해서 300개의 data를 보여준다.


공간을 어떻게 재활용 하는지에 대해 살펴보기 위해서는,

일단 RecyclerView의 구현 방법 부터 살펴보아야한다.


RecyclerView 안에는 총 23개의 inner class와 inner interface가 있다.

 

우리는 이 모든걸 다 쓰진 않을 것이고(^^)


가장 간단하게 LayoutManager, Adapter, ViewHolder 를 사용하여 list 화면을 만들 것이다.

그리고 그 이후에는, ItemDecoration과 ItemAnimator를 이용하여 RecyclerView를 더 이쁘게 만들어 볼 것이다.



1) 초 간단 RecyclerView 만들기

 


들어가기전에, 우리가 사용할 클래스들이 어디에 쓰이는지를 알아보자.

LayoutManger : RecyclerView내에 item view들의 크기를 측정하고, 위치를 지정하는 역할.

                  & 언제 item view들을 재사용해야하는지에 대한 정책을 결정하는 역할.

Adapter : RecyclerView 내에 보여지는 view들에 data set을 binding 시켜주는 역할.

ViewHolder : RecyclerView내 위치하는 곳의 item view와 metadata 정보를 갖고있는 역할.

               ViewHolder는 Adapter에 속해야 한다. (Adapter의 subclass로 viewholder가 구현되야한다)



그림으로 표현해보자면 다음과 같다!


 

 

RecyclerView가 있고, 그 내부의 item view들에 data들을 binding 시켜주는 역할은 adapter가 전담한다.

Adapter는 각 item view의 정보를 갖고 있는 ViewHolder를 이용해서 각각의 item view에 알맞은 data를 binding 시켜준다.

그리고 LayoutManager가 각 item view들의 크기를 측정하고 위치를 지정하여 RecyclerView를 구성한다.


item view의 내용을 위에 그림처럼 왼쪽에 숫자와 오른쪽에 안드로이드 아이콘이 아닌,

왼쪽에 사진과 오른쪽에 사람이름을 넣고 싶다면 어떻게 해야할까?


item view의 내용을 갖고 있는 것은 ViewHolder이므로, 또 다른 ViewHolder를 만들어야 할 것이다.

그리고 viewType이라는 것이 있는데, 이 viewType으로 어떤 viewHolder를 사용할 것인지를 판단 할 수 있다.


설명은 여기서 마치고, 이제부터는 코드로 정리하겠다.


MainActivity.java

public class MainActivity extends AppCompatActivity {

private RecyclerviewAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
adapter = new RecyclerviewAdapter();
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

}
}


RecyclerViewAdapter.java

public class RecyclerviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private ArrayList<String> items = new ArrayList<>();

public RecyclerviewAdapter() {
for (int i = 0; i < 20; i++) {
items.add("" + i);
}
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View view = null;
RecyclerView.ViewHolder viewHolder = null;

/*switch (viewType){
case 1:*/
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item1, parent, false);
viewHolder = new ItemOneViewHolder(view);
/* break;
case 2:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item2, parent, false);
viewHolder = new ItemTwoViewHolder(view);
break;
case 3:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item3, parent, false);
viewHolder = new ItemThreeViewHolder(view);
break;
default:
break;
}*/

return viewHolder;
}


@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//if(holder instanceof ItemOneViewHolder){
((ItemOneViewHolder)holder).text.setText(items.get(position));
/*}
else if(holder instanceof ItemTwoViewHolder){
((ItemTwoViewHolder)holder).text.setText(items.get(position));
}else if(holder instanceof ItemThreeViewHolder){
((ItemThreeViewHolder)holder).text.setText(items.get(position));
}*/
}


@Override
public int getItemCount() {
return items.size();
}

@Override
public int getItemViewType(int position) {
int viewType = 1;

/*switch (position%3){
case 0:
viewType = 1;
break;
case 1:
viewType = 2;
break;
case 2:
viewType = 3;
break;
}*/

return viewType;
}

class ItemOneViewHolder extends RecyclerView.ViewHolder {
TextView text;
ImageView icon;

public ItemOneViewHolder(View itemView) {
super(itemView);
text = (TextView)itemView.findViewById(R.id.text1);
icon = (ImageView)itemView.findViewById(R.id.icon1);
}
}

class ItemTwoViewHolder extends RecyclerView.ViewHolder {
TextView text;
ImageView icon;

public ItemTwoViewHolder(View itemView) {
super(itemView);
text = (TextView)itemView.findViewById(R.id.text2);
icon = (ImageView)itemView.findViewById(R.id.icon2);

}
}

class ItemThreeViewHolder extends RecyclerView.ViewHolder {
TextView text;
ImageView icon;

public ItemThreeViewHolder(View itemView) {
super(itemView);
text = (TextView)itemView.findViewById(R.id.text3);
icon = (ImageView)itemView.findViewById(R.id.icon3);

}
}
}

(일부러 ViewHolder를 여러개 만들어서, viewType별로 그에 맞는 viewHolder를 생성하는 코드를 주석처리하여 첨부하였다.)


Adapter는 RecyclerView 내의 모든 item view들의 데이터를 관리하는 역할을 한다고 하였다.


Adapter는,

onCreateViewHolder

onBindViewHolder

getItemCount

getItemViewType

이 4개의 메소드를 통해서 data를 binding한다.


호출 되는 순서대로 보자면,

getItemCount가 불리면서, 총 item 갯수가 몇개 인지 판단하고,

getItemViewType이 불리면서, 현재 item view의 position에 해당하는 viewType을 판단한다.


그 후 onCreateViewHolder에서, viewType에 해당하는 ViewHolder를 생성하여 return한다.

(각 item view의 정보를 갖고있는 애는 ViewHolder라고 하였다. 

 그러므로 Adapter는 계속해서 ViewHolder를 이용해 item view를 관리할 것 이다.)


onBindViewHolder에서는 생성된 viewHolder와 position을 전달받아서,

현재 position에 맞는 data를 viewHolder가 관리하는 view들에 binding 한다.


폰 화면에 10개의 list가 보인다면,

맨처음 getItemCount가 불리고,

그 후 10번 getItemViewType, onCreateViewHolder, onBindViewHolder가 연속적으로 호출 될 것이다.


간단히 보면 이렇고,

내부적으로 어떻게 동작하는지를 살펴보면,

view의 layout pass에서, LayoutManager의 onLayoutChildren가 호출되고,

이 메소드 내에서 타고타고 가다보면, Recycler 내의 getViewForPosition이라는 메소드가 호출되는데, (핵심적인 메소드이다)

이 부분에서 Adapter의 4개의 메소드가 설명한 순서대로 불리게 된다.



RecyclerView의 꽃인 view 재사용에 대해서 설명하면서, 더 자세히 알아보자.


화면의 스크롤을 내려서 11번, 12번, 13번의 item view가 보이게 된다면 내부적으로 어떤 동작이 이루어질까?

여기서, scrapped view라는 개념이 사용된다.

scrapped view란, 아직 RecyclerView에는 붙어있지만, 곧 제거되거나 재사용될 것으로 지정된 viewHolder이다.


RecyclerView내의 Recycler라는 class는 scrapped view들을 관리하여 재사용하는데 책임이 있는 class이다.

그리고 이 Recycler는, scrapped view들의 pool을 관리하는 RecycledViewPool class를 이용하여 scrapped view를 관리한다.

기억을 되살려보면 초반에, LayoutManager가 item view들을 언제 재사용할지에 대한 정책을 결정한다고 했었다.


즉, LayoutManager가 어떤 viewholder를 scrapped view로 지정할 것인지에 대해 책임을 지고,

Recycler는 LayoutManager에서 scrapped view로 지정한 viewholder들을 RecycledViewPool을 통해서 관리한다고 생각하면 되겠다.


 


스크롤을 아래로 내리면, LinearLayoutManger의 scrollVerticallyBy 메소드가 호출되고, 

이 메소드 내에서 타고타고 가다보면, 어떤 viewholder를 scrapped view로 지정할 것인지에 대해서 결정하고,

(스크롤을 아래로 내리면, 맨 위에 애들부터 scrapped view로 지정된다.)

그 후 위에서 언급했던 Recycler 내의 getViewForPosition이라는 메소드가 호출된다.


Recycler의 getViewForPosition 메소드는 현재 LayoutManager가 배치하고자 하는 view의 position을 전달인자로 받아서,

이 position에 맞는 view를 반환하는 메소드이다.


Recycler는 position에 알맞는 view를 판단하기 위해서, 

일단 adapter의 getItemCount를 통해서 아이템 수를 얻어와 유효한 position인지 체크하고,

getItemViewType을 호출해서 현재 position의 viewType을 알아낸다.


그리고, onCreateViewHolder를 하기전에, 

scrapped view pool에 현재 viewType과 같은 ViewHolder가 있다면 그 ViewHolder를 얻어오고 create하지 않는다.

(없으면 생성해야겠지요?)

이게 바로, RecyclerView가 공간을 재사용하는 방법이다!!

기존에 생성되어있던 ViewHolder 중 더 이상 사용되지 않는 ViewHolder를 가지고 와서 쓰는 것이다.


이제 마지막으로 onBindViewHolder를 호출하여 viewHolder를 인자로 넘겨주어 viewHolder내의 view들에 data를 binding한다.


출처 http://blog.naver.com/PostView.nhn?blogId=mail1001&logNo=220682221473



윈도우에서는 F4를 누르면 SDK Location을 눌러보면 위치가 나온다.


안드로이드 스튜디오 SDK path 설정 및 JDK path 설정


안드로이드 스튜디오를 사용하다보면 아주 가~끔 SDK path 나 JDK path를 변경해줘야 하는 경우가 발생합니다.

이럴때는 다음과 같이 따라해보세요~


1. File - Close Project를 클릭하여 열려있는 프로젝트를 닫습니다.


2.Configure 클릭



3.Project Defaults 클릭



4.Project Structure 클릭



5.SDK Location에서 본인의 SDK path 및 JDK path를 설정해주시면 끝!!


참쉽죠잉~~


이상 안드로이드 스튜디오 SDK path 설정 및 JDK path 설정하는 방법이었습니다.

말복이 지나서인지 오늘은 그리 덥지 않네요

내일 대체 휴일이네요

뉴스에 보니깐 10명중 3명은 쉬지 못한다고...

그나마 제가 다니는 회사는 공기업도 아닌데 쉬는걸 보면 그리 나쁘지만은 않은가 보네요

모두들 힘내세요~^^


TransculentStatusBar를 이용하면 된다.


사용법


1. 윈도우창 부분 설정

getWindow().getDecorView().setSystemUiVisibility(
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

2. /res/values-v21/styles.xml 스타일 설정


<item name="android:statusBarColor">@android:color/transparent</item>

3. getWindow().setStatusBarColor(Color.TRANSPARENT);


해당 스타일로 적용하기 위해서 컬러는 투명 상태입니다. 스타일 폴더에 실제로 기기 디바이스에 해당되는 사이즈가 별도로 있으니 확인 바랍니다.

canvas 안드로이드 화면에 그릴 도화지 cavas를 사용하기 위해서는 onDraw에서 그려주면 된다.
주요 메소를 살펴보면

canvas.save() 캔버스에 무언가를 그리기전에 캔버스 자체가 회전, 필터등을 설정하고, 그리기 툴을 지정한다.

oDraw 로 그림을 그려낸다. 이전에 그리기 툴을 저장했던것을 복원할려면 canvas.restore()를 사용한다



'Developer > Android' 카테고리의 다른 글

안드로이드 스튜디오 sdk 경로  (0) 2016.08.26
상태바 투명하게 만들기  (0) 2016.08.26
PaintFlagsDrawFilter  (0) 2016.08.26
GestureDetector  (0) 2016.08.26
TextView 상단 여백 제거하기  (0) 2016.08.26


android api

Subclass of DrawFilter that affects every paint by first clearing the specified clearBits in the paint's flags, and then setting the specified setBits in the paint's flags.



cv.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG))


캔버스의 그림 영역에 안티 엘리언싱 필터를 먹여주는 함수입니다. 이미지가 확대되거나 축소될때 생기는 계단현상을 최대한 부드럽게 처리해주는 함수로.. 저렇게 직접 안드로이드에서 지원하는 클래스로 필터를 생성하여 적용시킬 수도 있지만, Paint 객체에도 setAntiAlias(boolean) 함수가 있어 이 함수로 셋팅하고 뿌려줄때 인자로 넣어줘도 됩니다



[출처] 안드로이드사이드 - http://www.androidside.com/bbs/board.php?bo_table=B49&wr_id=36195#c_36223

'Developer > Android' 카테고리의 다른 글

안드로이드 스튜디오 sdk 경로  (0) 2016.08.26
상태바 투명하게 만들기  (0) 2016.08.26
android canvas 정리  (0) 2016.08.26
GestureDetector  (0) 2016.08.26
TextView 상단 여백 제거하기  (0) 2016.08.26

GestureDetector.SimpleOnGestureListener에 onSingleTapUp 있다.


onSingleTapUp를 통해서 터치가 한번만 되었는지 알 수 있음


'Developer > Android' 카테고리의 다른 글

안드로이드 스튜디오 sdk 경로  (0) 2016.08.26
상태바 투명하게 만들기  (0) 2016.08.26
android canvas 정리  (0) 2016.08.26
PaintFlagsDrawFilter  (0) 2016.08.26
TextView 상단 여백 제거하기  (0) 2016.08.26

글씨가 커짐에 따라 여백이 증가한다


자바 소스

- setIncludeFontPadding( false );


xml 소스

- android:includeFontPadding="false" 

'Developer > Android' 카테고리의 다른 글

안드로이드 스튜디오 sdk 경로  (0) 2016.08.26
상태바 투명하게 만들기  (0) 2016.08.26
android canvas 정리  (0) 2016.08.26
PaintFlagsDrawFilter  (0) 2016.08.26
GestureDetector  (0) 2016.08.26

+ Recent posts