728x90
반응형

 

간단하게 유저정보를 저장하고 간략히 보여주는 걸 만들자

 

Layout 구성

fragment_userinfo.xml

저번에 화면전환에서 만들었던 TextView를 지우고 title과 list를 만들어준다

<?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">

    <TextView
        android:id="@+id/mini_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#ECF0F1"
        android:padding="10dp"
        android:text="사용자 상세정보"
        android:textSize="25dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0" />

    <ListView
        android:id="@+id/userInfo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/mini_title"
        app:layout_constraintVertical_bias="0.0"
        tools:ignore="MissingConstraints" />


</androidx.constraintlayout.widget.ConstraintLayout>

 

그리고 homeFragment와 같이 listitem에 대한 layout을 만들어주자

userinfolist.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0">

        <TextView
            android:id="@+id/valueName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Name"
            android:textSize="25dp"
            android:textColor="#000000"
            android:layout_gravity="left"
            android:padding="20dp"/>

        <View
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="1"/>

        <TextView
            android:id="@+id/value"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="value"
            android:textSize="25dp"
            android:textColor="#C4C4C4C4"
            android:layout_gravity="right"
            android:padding="20dp"/>
    </LinearLayout>



</androidx.constraintlayout.widget.ConstraintLayout>

 

userInfoFragment.java

일단 저번과 같이 그대로 LIST를 정의하고 adpater를 만들어서 listview에 넣어봤다

...    
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_userinfo, container, false);

        final String[] LIST_NAME = {"키/몸무게","나이", "성별", "BMI", "운동시작일"};
        final String[] LIST_VALUE = {"180cm/80.0kg","18세", "남", "24.69(정상)", "2021.07.29"};

        ArrayAdapter adapter1 = new ArrayAdapter(getActivity(), R.layout.userinfolist,R.id.valueName, LIST_NAME);
        ArrayAdapter adapter2 = new ArrayAdapter(getActivity(), R.layout.userinfolist, R.id.value, LIST_VALUE);

        ListView listview = (ListView) view.findViewById(R.id.userInfo);
        listview.setAdapter(adapter1);
        listview.setAdapter(adapter2);

        return view;
    }
    ...

아쉽게도 setAdapter를 하나만 되는듯 하다

 

https://gg-i-dont-know.tistory.com/8

https://recipes4dev.tistory.com/43

 

위 두 글에서 두 개 이상의 값을 추가하는 방법을 발견했다

ListItem.java

단순한 set/get 함수가 있는 클래스

package com.lektion.gympartner;

public class ListItem {
    private String Name;
    private String Value;

    public void setName(String name) {
        Name = name;
    }

    public void setValue(String value) {
        Value = value;
    }

    public String getName() {
        return this.Name;
    }

    public String getValue() {
        return this.Value;
    }
}

 

ListViewAdapter.java

더보기
package com.lektion.gympartner;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.ArrayList;

public class ListViewAdapter extends BaseAdapter {
    private Context mContext;
    private ArrayList<ListItem> listItems = new ArrayList<>();

    public ListViewAdapter(Context context)
    {
        this.mContext = context;
    }

    @Override
    public int getCount() {
        return listItems.size();
    }

    @Override
    public Object getItem(int position) {
        return listItems.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null)
        {
            LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.userinfolist,parent,false);
        }

        TextView txt_name = (TextView) convertView.findViewById(R.id.valueName);
        TextView txt_value = (TextView) convertView.findViewById(R.id.value);

        ListItem listItem  = listItems.get(position);

        txt_name.setText(listItem.getName());
        txt_value.setText(listItem.getValue());

        return convertView;
    }

    public void addItem(String name, String value)
    {
        ListItem listItem = new ListItem();

        listItem.setName(name);
        listItem.setValue(value);

        listItems.add(listItem);
    }
}

 

기본적인 Override 함수

 

context 받아오기

 

listview가 있는 레이아웃(userinfolist)를 converView로 참조

그리고 필요한 TextView를 찾고 Text를 설정

 

리스트에 아이템(name과 value)을 추가

 

 

그리곤 userInfoFragment를 다음과 같이 수정

public class userInfoFragment extends Fragment {

    ListView userinfoList;
    ListViewAdapter adapter;

    float height = 180.0F;
    float weight = 80.0F;
    int age = 18;
    String gender = "남";
    float BMI = 0F;
    String BMItext = "정상";
    Date startDay;

    @SuppressLint("NewApi")
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_userinfo, container, false);

        BMI = (float) (weight/Math.pow(((height)/100),2));
        startDay = Calendar.getInstance().getTime();

        SimpleDateFormat format = new SimpleDateFormat("yyyy.MM.dd", Locale.KOREA);

        adapter = new ListViewAdapter(getActivity());

        userinfoList = (ListView) view.findViewById(R.id.userInfo);
        userinfoList.setAdapter(adapter);

        adapter.addItem("키/몸무게", Float.toString(height) + "cm/" + Float.toString(weight) + "kg");
        adapter.addItem("나이", Integer.toString(age) + "세");
        adapter.addItem("성별", gender);
        adapter.addItem("BMI", String.format("%.2f", BMI) + "(" + BMItext + ")");
        adapter.addItem("운동시작일", format.format(startDay));

        return view;
    }

이따가 저장된 데이터를 바탕으로 text를 설정하므로 모두 변수로 설정해두고

각각의 값들을 addItem을 통해 ListView에 추가

 

실행하면 다음과 같이 나온다

 

유저 데이터 수정

AlertDialog

AlertDialog는 이런거다

테스트겸 간단한 메세지를 보여주는 예제로 만들었다

 

listview에 있느 item를 클릭하는 이벤트는 OnItemClickListner를 통해 처리한다

userinfoList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                
                builder.setTitle("builder title");
                builder.setMessage("builder message");
                
                AlertDialog alertDialog = builer.create();
                alertDialog.show();
            }
        });

 

이제 저걸 수정하는 창으로 만들자

layout_editwh

먼저 dialog의 layout을 만들자

더보기
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="신장"
            android:textSize="15dp"
            android:paddingRight="10dp"/>
        <EditText
            android:id="@+id/height"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="5"
            android:inputType="number"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="cm"
            android:paddingLeft="10dp"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="몸무게"
            android:textSize="15dp"
            android:paddingRight="10dp"/>
        <EditText
            android:id="@+id/weight"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="5"
            android:inputType="number"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="kg"
            android:paddingLeft="10dp"/>
    </LinearLayout>

</LinearLayout>

TextView와 EditText를 이용해 적당히 만들어 주었다

 

다음은 이 layout을 dialog로 띄우기

View dialogView = getLayoutInflater().inflate(R.layout.layout_editwh, null);

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setView(dialogView);

        AlertDialog alertDialog = builder.create();
        alertDialog.show();

builder.setView를 통해 dialog의 layout를 선택할 수 있다

 

다음은 확인버튼을 만들자

dialogView에 있는 EditText를 가져와서 확인버튼을 누르면 각 값들을 height와 weight에 저장한다

final EditText hEditText = (EditText) dialogView.findViewById(R.id.height);
final EditText wEditText = (EditText) dialogView.findViewById(R.id.weight);

builder.setPositiveButton("확인", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                height = Float.parseFloat(hEditText.getText().toString());
                weight = Float.parseFloat(wEditText.getText().toString());
            }
        });

그리고 아무것도 입력하지 않는 걸 방지하기위해 아래 코드를 추가해주었다

현재 height와 weight를 EditText의 기본값으로 설정한다

hEditText.setText(Float.toString(height));
wEditText.setText(Float.toString(weight));

 

하지만 확인버튼을 눌러도 바뀌지 않았다

...

 

변수만 수정하고 listview는 수정하지 않았다

 

ListViewAdapter.java에 수정하는 함수를 작성하자

    public void modifyItem(int position,  String value)
    {
        ListItem listItem = listItems.get(position);

        listItem.setValue(value);
    }

그리고 확인 버튼 이벤트에 코드를 추가

builder.setPositiveButton("확인", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                height = Float.parseFloat(hEditText.getText().toString());
                weight = Float.parseFloat(wEditText.getText().toString());

                adapter.modifyItem(0,Float.toString(height) + "cm/" + Float.toString(weight) + "kg");
            }
        });

왜 안되는 거지...

 

찾아보니 바뀐 데이터를 listview에 적용하는 함수가 있었다

아래 코드를 추가

adatper.nofiyDataSetChanged();

 

입력한대로 list의 값이 바뀌는걸 볼 수 있다.

 

다만 클릭했을 때 전체선택이 안되는게 좀 불편하다

그리고 소수점 입력이 되지 않는다

이것은 layout_editwh에서 해결할 수 있었다

 

각각 EditText에 아래 두 속성을 추가한다

android:inputType="numberDecimal"
android:selectAllOnFocus="true"

 

그리고 완료버튼을 누를 때 다음으로 넘어가거나 입력창을 닫는 속성도 추가해주었다

android:imeOptions="actionNext"

android:iemOpeions="actionDone"

 

같은 방식으로 나이도 설정할 수 있게 만들자

 

다음은 성별선택

성별선택은 그냥 radioButton으로 하자

private void showGender() {
        final String[] items = new String[]{"남", "여"};

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if(which == 0)
                {
                    gender = "남";
                }
                else
                {
                    gender="여";
                }
            }
        });

        builder.setPositiveButton("확인", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                adapter.modifyItem(2, gender);
                adapter.notifyDataSetChanged();
            }
        });

        builder.setNegativeButton("취소", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });

        AlertDialog alertDialog = builder.create();
        alertDialog.show();

그리고 각각 함수에 취소버튼을 negativeButton으로 추가해주었다

 

마지막으로 신장과 체중 변화에 따른 BMI지수도 변경해주자

 

showWH()함수의 버튼 클릭 이벤트에서 BMI관련 코드를 추가

        builder.setPositiveButton("확인", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                height = Float.parseFloat(hEditText.getText().toString());
                weight = Float.parseFloat(wEditText.getText().toString());
                BMI = (float) (weight / Math.pow(((height) / 100), 2));
                BMItext = calcBMI();

                adapter.modifyItem(0, Float.toString(height) + "cm/" + Float.toString(weight) + "kg");
                adapter.modifyItem(3, String.format("%.2f", BMI) + "(" + BMItext + ")");
                adapter.notifyDataSetChanged();
            }
        });


    private String calcBMI()
    {
        String _value = "";
        if(BMI < 18.5F)
        {
            _value = "저체중";
        }
        else if(BMI < 23F)
        {
            _value = "정상";
        }
        else if(BMI < 25F)
        {
            _value = "과체중";
        }
        else
        {
            _value = "비만";
        }
        return _value;
    }

BMI지수 계산은 아래 사이트를 따랐다

https://health.seoulmc.or.kr/healthCareInfo/myBMIPopup.do

나이까지 계산하는건 너무 귀찮...

 

그러나 이렇게 listview의 값을 바꿔도 앱을 종료했다가 다시 실행하면 기본값으로 돌아온다

데이터를 앱에 저장한 것이 아니기 때문이다

그건 그렇고 왜 글자는 깨지는거지...

SharedPreferences

https://www.youtube.com/watch?v=-2QPmS4OWos 

SharedPreferences는 간단한 데이터를 저장하는 방식이라고 한다

Dictionary와 작동방식이 거의 유사하다

저장되는 경로는 아래와 같이 해당 애플리케이션 폴더에 저장되기 때문에 앱을 삭제하면 데이터도 사라진다 

data/data/(package_name)/shared_prefs/SharedPreference

 

먼저 sharedPreference를 선언한다

첫번째 매개변수가 이 데이터들이 저장되는 폴더의 이름인듯 하다

SharedPreferences sharedPreferences = getActivity().getSharedPreferences("file", 0);

 

다음으로 editor를 선언한다

editor는 저장된 SharedPreferences에 정보를 추가, 삭제, 수정할 수 있다

SharedPreferences sharedPreferences = getActivity().getSharedPreferences(shared, 0);

 

그 다음은 저장할 데이터를 추가한다

editor.putFloat("height", height);
editor.putFloat("weight", weight);
editor.putInt("age", age);
editor.putString("gender", gender);
editor.putFloat("BMI", BMI);
editor.putString("BMItext", BMItext);
editor.putString("date", startDay);

put[dataType]를 통해 여러가지 변수들을 저장할 수 있다

 

마지막으로 데이터를 저장한다

editor.commit();

 

마치 git과 같은 느낌이다

editor.put[dataType]은 git add

editor.commit()은 git commit

 

데이터를 불러올때는 아래와 같이 사용한다

height = sharedPreferences.getFloat("height", 180.0F);

첫번째 매개변수는 저장할 때 지정한 key값, 두번째 매개변수는 데이터가 없을 때의 변수값(defValue)이다

 

 

데이터를 불러오는 부분은 모두 onCrateView에 추가

데이터를 저장하는 부분은 onDestroy에 추가

 

하지만 현재 작업하는 코드가 Fragment였기에 저장은 Fragment가 닫힐때만 발생한다

즉 dialog로 입력하고 그냥 꺼버리면 저장이 되지 않는다...

 

그래서 저장하는 부분은 dialog의 onClick함수에 넣어주었다

 

성공!

 

마지막으로 navigation drawer의 사용자 정보를 수정하는 코드를 추가하자

onDestroy()에 추가했다

Fragment에서는 getActivity()로 MainActivity를 받아온 다음에 findViewById로 nav_view를 찾고

nav_view에 있는 Item들을 찾아서 title를 바꿔준다

NavigationView navigationView = (NavigationView) getActivity().findViewById(R.id.nav_view);

MenuItem heightText = navigationView.getMenu().findItem(R.id.menu_height);
MenuItem weightText = navigationView.getMenu().findItem(R.id.menu_weight);

heightText.setTitle("키: " + height + "cm");
weightText.setTitle("몸무게: " + weight + "kg");

 

그리고 MainActivity에도 추가해준다

getActivity()만 빼고 onCreate에 추가하면 된다

여기에도 추가하는 이유는 userInfoFragment를 종료하지 않고 앱을 종료하면 onDestroy함수가 실행이 안되기 때문에

title이 바뀌지 않는다

 

진짜 끝!

 

 

다음에는 운동목록과 루틴추가 쪽을 시도해보자

원래 방학 내로 끝내려고 했으나 실패...