profile image

L o a d i n g . . .

반응형

안드로이드 코틀린 그리드뷰 사용하기

Android Kotlin GridView

 

가장 많이 사용할 것 같지만 두어번 밖에 사용안해본 gridview 이를 이용하면 간단하게 한 줄에 여러개의 아이템을 넣어서 보여줄 수 있는데, 다른 방법도 구현이 가능하지만, 안드로이드에서 제공해주는 gridview를 이용하면 더 쉽게 구현할 수 있다.

따로 implement도 할 필요가 없다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <GridView
        android:id="@+id/gridView"
        android:numColumns="auto_fit"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

gridview도 다른 뷰들과 마찬가지로 특별한 점이 없지만 numColumns만 신경써서 설정을 해주면 된다.

auto_fit의 경우 알아서 크기에 맞게 들어가므로 저렇게 구현을 하였지만, 3개나 4개를 넣어야 할 경우 3 혹은 4라고 넣으면 된다.

 

class FoodAdapter(private var context: Context?, private var foodList: ArrayList<Food>) : BaseAdapter() {

    @SuppressLint("ViewHolder")
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val food : Food = foodList[position]
        val inflater = context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        val foodView : View = inflater.inflate(R.layout.mylayout, null)
        foodView.imageView.setBackgroundResource(food.image!!)
        foodView.textView.text = food.name!!

        foodView.imageView.setOnClickListener {
            val intent = Intent(context, FoodDetailActivity::class.java)
            intent.putExtra("name", food.name!!)
            intent.putExtra("des", food.des!!)
            intent.putExtra("image", food.image!!)
            context!!.startActivity(intent)
        }
        return foodView
    }

    override fun getItem(position: Int): Any {
        return position
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getCount(): Int {
        return foodList.size
    }

}

gridview의 아답터(Adapter)는 다른 아답터와는 달리 BaseAdpater를 상속받아서 구현하면 된다.

생성자의 경우 따로 constructor()를 구현할 수 있겠지만, kotlin의 기능을 사용하여 따로 구현하지 않고 클래스에다가 직접 구현을 하였다.

그러므로 생서자에서 this를 쓸 필요도 없이 바로 받아서 사용할 수 있다.

 

아답터를 사용하기 위해서는 4개의 메소드를 오버라이드 해야 하는데, getView, getitem, getItemId, getcount를 구현하면 된다.

getItemId의 경우 타입이 Long으로 되어 있어서 타입을 toLong()을 사용하여 캐스팅을 해주어야 한다.

왜 getItemId만 타입이 Long인지는 모르겠다..

 

context를 생성자에서 넘겨 받은 이유는 intent를 사용하여 클릭했을 경우 다른 뷰로 이동하기 위해서와 레이아웃을 인플레이터 하기 위하여 받았다. 

인텐트에는 이미지와 제목, 내용을 받아서 디테일 엑티비티로 넘겨주도록 하였다.

 

foodList에서 각 인덱스의 데이터를 가져오기 위해여 foodList[position]을 사용하였다, java에서는 foodList.get(position)을 사용하여야 했겠지만, kotlin으로 넘어와서 []을 사용하여 index를 넣으면 index에 맞는 데이터를 가져올 수 있다.

위와같이 구현을 완료하면 위와 같은 이미지의 간단한 결과물을 볼 수 있다.

 

참고로 데이터 클래스에는 이미지와 이름, 내용 이렇게 3개만 있다. 내용의 경우 클릭했을 때 디테일 페이지에서 사용할려고 했다.

 

class FoodDetailActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_food_detail)

        val intent = intent.extras
        imageView2.setBackgroundResource(intent!!.getInt("image"))
        textView2.text = intent.getString("name")
        textView3.text = intent.getString("des")
    }
}

클릭했을 때 디테일 엑티비티는 큰 특징없이 intent로 보낸 데이터를 받아서 그대로 뿌려주고 있다.

intent로 받을 수 있지만 Bundle로 받아서 사용할 수 도 있다.

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    @UnsupportedAppUsage
    private final DataSetObservable mDataSetObservable = new DataSetObservable();
    private CharSequence[] mAutofillOptions;

    public boolean hasStableIds() {
        return false;
    }
    
    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
    
    /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

    /**
     * Notifies the attached observers that the underlying data is no longer valid
     * or available. Once invoked this adapter is no longer valid and should
     * not report further data set changes.
     */
    public void notifyDataSetInvalidated() {
        mDataSetObservable.notifyInvalidated();
    }

    public boolean areAllItemsEnabled() {
        return true;
    }

    public boolean isEnabled(int position) {
        return true;
    }

    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return getView(position, convertView, parent);
    }

    public int getItemViewType(int position) {
        return 0;
    }

    public int getViewTypeCount() {
        return 1;
    }
    
    public boolean isEmpty() {
        return getCount() == 0;
    }

    @Override
    public CharSequence[] getAutofillOptions() {
        return mAutofillOptions;
    }

    /**
     * Sets the value returned by {@link #getAutofillOptions()}
     */
    public void setAutofillOptions(@Nullable CharSequence... options) {
        mAutofillOptions = options;
    }
}

개인적으로 BaseAdapter가 너무 생소하여 보았더니 역시나 RecyclerView때문에 잘 사용하지 않는 ListView나 SpinnerView를 사용할 때 만드는 Adpater에서 사용하는 Adapter였다.

 

반응형
복사했습니다!