프로젝트/Android Project) 가장 가까운 주유소 찾기 app

가장 가까운 주유소 찾기 (4차 업데이트) 내용 (1)

roder 2023. 9. 21. 15:11

https://github.com/leeugun123/find_gas_station

 

GitHub - leeugun123/find_gas_station: find_gas

find_gas. Contribute to leeugun123/find_gas_station development by creating an account on GitHub.

github.com

 

# 4차 배포 내용 

 

1 .  Bottom Navigation Bar 생성

바텀 네비게이션 바는 어플의 기능과 사용자 경험을 향상시키는 필수 요소이다. 이를 통해 주유소 찾기 기능 뿐만 아니라 사용자가 원하는 다른 기능을 추가로 구현하기 위해 프래그먼트를 도입할 수 있다.

 

바텀 네비게이션바 도입

 

기존 액티비티에 있던 코드를  프래그먼트로  이전하려고 할때 고려해야 할 요소를 생각해보았다.

 

1-1 .  프래그먼트의 onCreateView() 

 

프래그먼트에서는 액티비티와 달리 프래그먼트의 UI를 그리기 위해

호출되는 onCreateView() 메소드가 있다.

이 메소드에서는 UI 요소를 inflate 하거나 생성하며 데이터를 바인딩한다.

여기서 반환한 View 객체가 프래그먼트의 UI가 된다.

액티비티에서 UI를 참고하는 요소인 버튼 클릭 콜백 메소드 , findViewById 등

UI와 관련된 코드를 모두 OnCreateView로 이전시켰다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
 @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
 
        Log.e("TAG","HomeFragment onCreateView");
 
 
        View rootView = inflater.inflate(R.layout.fragment_home, container, false);
 
        SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
 
        if (mapFragment == null) {
            mapFragment = SupportMapFragment.newInstance();
            getChildFragmentManager().beginTransaction()
                    .replace(R.id.map, mapFragment)
                    .commit();
        }//googleMap UI 추가
 
        mapFragment.getMapAsync(this);
        
 
        mRecyclerView = rootView.findViewById(R.id.list_recycle);
 
        mRecyclerView.setLayoutManager(new LinearLayoutManager(requireActivity(),RecyclerView.VERTICAL,false));
 
 
        progressBar = rootView.findViewById(R.id.progressBar);
 
        mLayout = rootView.findViewById(R.id.layout_main);
 
        array_first = rootView.findViewById(R.id.array_first);
 
 
        reset = rootView.findViewById(R.id.reset);
 
        reset.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
 
                init_reset();
                upRecyclerView();
 
            }
        });
 
        Setting = rootView.findViewById(R.id.setting);
 
        Setting.setOnClickListener(new View.OnClickListener() {
            @SuppressLint("ResourceType")
            @Override
            public void onClick(View view) {
 
                Intent intent = new Intent(requireActivity(), setting_Activity.class);
                startActivityForResult(intent,REQUEST_CODE);
 
            }//Setting activity로 전환
 
 
        });//메뉴 버튼 생성
 
 
        return rootView;
 
    }
 
cs

 

 

1-2 .  Fragment의 트랜잭션

Navigation 액티비티에서 바텀 네비게이션 바를 통해 프래그먼트를 replace 하면

주유소 정보를 가져오는 getData() 메소드가 쓸데없이 재호출 되는 문제가 발생한다.

따라서 액티비티에 다음과 같은 코드를 통해 프래그먼트 간 트랜잭션이 일어날때마다

재호출이 되지 않도록 변경하였다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
                switch (menuItem.getItemId()) {
 
                    case R.id.Home_fragment:
 
                        if(fa == null){
                            fa = new HomeFragment();
                            fragmentManager.beginTransaction().add(R.id.main_frame,fa).commit();
                        }
 
                        if(fa != null)
                            fragmentManager.beginTransaction().show(fa).commit();
                        if(fb != null)
                            fragmentManager.beginTransaction().hide(fb).commit();
 
                        break;
 
                    case R.id.Daily_fragment:
 
                        if(fb == null){
                            fb = new DailyFragment();
                            fragmentManager.beginTransaction().add(R.id.main_frame, fb).commit();
                        }
 
                        if(fa != null)
                            fragmentManager.beginTransaction().hide(fa).commit();
                        if(fb != null)
                            fragmentManager.beginTransaction().show(fb).commit();
 
                        break;
                }
 
                return true;
            }
        });
cs

 

다음과 같이 transation을 replace하는 것이 아닌 2개의 fa , fb

객체를 생성하고 동시에 유지한채

프래그먼트를 누를 때마다 show(), hide()로 이전 데이터를

계속 유지시키고 재호출을 방지하였다.

하지만 replace를 사용하지 않으면 프래그먼트 객체들이 메모리에

계속 유지된채로 남아있기 때문에 메모리 누수에 주의해야 한다. 

또한 추가해야하는 프래그먼트가 늘어날 경우,

코드의 복잡성이 증가하여 추후에 유지보수가 힘들어질 수 도 있다. 

물론 replace와 show,hide 중 어떤 것이 더 나은가?에 대한 질문에는

앱의 요구사항과 성능에 따라 적절하게 배치하는 것이 좋을 것 같다.

 

 

2 .  평균 전국 유가 정보 가져오기

 

유저들이 가장 원하는 정보가 무엇일까? 고민하다가

전국 기름 값의 평균 가격이 아닐까 판단하였다.

물론 각 지역마다 유가 평균 가격을 보여주면 좋겠지만,

복잡성이 증가할까봐 일단 범위를 전국으로 정하여 유가 정보를

사용자에게 보여주기로 기획하였다.

 

2-1.  탭 생성

 

 

각 탭마다 Fragment를 생성하여 이전과 같이 데이터를 유지하게 하였다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        gasol_Fragment = new GasolineFragment();
        disel_Fragment = new DieselFragment();
        HighGasol_Fragment = new High_GasolineFragment();
        kerosene_Fragment = new KeroseneFragment();
        butan_Fragment = new ButaneFragment();
 
        // 처음에는 가솔린 프래그먼트를 보여줍니다.
        currentFragment = gasol_Fragment;
        getParentFragmentManager().beginTransaction().add(R.id.frame, currentFragment).commit();
 
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
 
        View rootView = inflater.inflate(R.layout.fragment_daily, container, false);
 
        tabs = rootView.findViewById(R.id.tabs);
 
        tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                int position = tab.getPosition();
 
                Fragment selected = null;
 
                if (position == 0) {
                    selected = gasol_Fragment;
                } else if (position == 1) {
                    selected = disel_Fragment;
                } else if (position == 2) {
                    selected = HighGasol_Fragment;
                } else if (position == 3) {
                    selected = kerosene_Fragment;
                } else {
                    selected = butan_Fragment;
                }
 
                // 현재 보여지고 있는 프래그먼트와 선택된 프래그먼트가 같으면 아무 작업을 하지 않습니다.
                if (currentFragment == selected) {
                    return;
                }
 
                // 선택된 프래그먼트가 이전에 생성된 적이 있는지 확인합니다.
                if (!selected.isAdded()) {
                    // 선택된 프래그먼트가 이전에 생성된 적이 없다면 추가합니다.
                    getParentFragmentManager().beginTransaction().add(R.id.frame, selected).hide(currentFragment).commit();
                } else {
                    // 선택된 프래그먼트가 이전에 생성된 적이 있다면 숨겨진 프래그먼트를 보여줍니다.
                    getParentFragmentManager().beginTransaction().show(selected).hide(currentFragment).commit();
                }
 
                currentFragment = selected; // 현재 프래그먼트를 선택된 프래그먼트로 업데이트합니다.
            }
 
            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
 
            }
 
            @Override
            public void onTabReselected(TabLayout.Tab tab) {
 
            }
        });
 
        return rootView;
    }
cs

 

2-2.   차트 생성

각  기름의 일주일간 주유 정보를 text로만 보여주는 것이 차트를 이용하여

사용자가 한눈에 유가 변화를 알아 볼 수 있도록 하고 싶었다.

적절한 차트 라이브러리를 찾다가 MPAndroidChart를 사용하였고,

다음과 같이 코드를 작성하여 차트 변화를 통해 유가 변화를

한눈에 알아볼 수 있도록 구현하였다.

 

MPAndroidChart : https://github.com/PhilJay/MPAndroidChart

 

GitHub - PhilJay/MPAndroidChart: A powerful 🚀 Android chart view / graph view library, supporting line- bar- pie- radar- bubb

A powerful 🚀 Android chart view / graph view library, supporting line- bar- pie- radar- bubble- and candlestick charts as well as scaling, panning and animations. - GitHub - PhilJay/MPAndroidChart:...

github.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
 public void getOilAvg(LineChart lineChart, RecyclerView oilAvg_recyclerView, TextView priceText, String prodcd){
 
        opinet_retrofitApi.getAvgRecentPrice(opinet_apiKey,"json",prodcd)
                .enqueue(new Callback<OilPriceInfo>() {
 
                    @SuppressLint("SetTextI18n")
                    @Override
                    public void onResponse(Call<OilPriceInfo> call, Response<OilPriceInfo> response) {
 
                        if(response.isSuccessful()){
 
                            OilPriceInfo oilPriceInfo = response.body();
                            OilPriceInfo.Result result = oilPriceInfo.getRESULT();
 
                            List<Entry> entries = new ArrayList<>();
                            ArrayList<OilPriceInfo.OilPrice> Avg = new ArrayList<>();
 
                            for(int i=0; i<result.getOIL().size(); i++){
 
                                OilPriceInfo.OilPrice oilPrice = result.getOIL().get(i);
 
                                //recyclerView에 담을 ArrayList<Oil>
                                Avg.add(new OilPriceInfo.OilPrice(
                                        oilPrice.getDATE(),
                                        (int) oilPrice.getPRICE()
                                ));
 
                                entries.add(new Entry(i, (int) oilPrice.getPRICE()));
                            }
 
                            LineDataSet dataSet = new LineDataSet(entries, "주유소 가격");
                            dataSet.setColor(Color.rgb(255,153,000));
                            dataSet.setLineWidth(2f);
                            dataSet.setCircleColor(Color.rgb(255,153,000));
                            dataSet.setCircleRadius(4f);
                            dataSet.setDrawCircleHole(false);
 
                            // Example: Customize the x-axis labels
                            XAxis xAxis = lineChart.getXAxis();
                            xAxis.setValueFormatter(new IndexAxisValueFormatter(new String[]{"7일전""6일전""5일전""4일전""3일전",
                            "2일전","1일전"}));
 
                            List<ILineDataSet> dataSets = new ArrayList<>();
                            dataSets.add(dataSet);
 
                            LineData lineData = new LineData(dataSets);
 
                            lineChart.setData(lineData);
                            lineChart.getDescription().setText("최근 일주일 전국 유가 가격");
                            lineChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
                            lineChart.getAxisRight().setEnabled(false);
                            lineChart.invalidate();
 
                            Collections.reverse(Avg);
                            //역순 뒤집기
 
                            if(Avg.size() > 0)
                                priceText.setText(Integer.toString( (int) Avg.get(0).getPRICE()));
 
                            oilAvg_recyclerView.setAdapter(new OilAvgRecyclerAdapter(Avg));
 
                        }
 
 
                    }
 
                    @Override
                    public void onFailure(Call<OilPriceInfo> call, Throwable t) {
 
 
 
                    }
 
 
 
                });
 
 
    }
cs

 

2-3.   날짜 간 가격 변화 구현

리사이클러뷰를 보면 각 날짜마다 이전 날과 가격 차이를 표시해주는 View가 있다.

어뎁터 안에 매개변수를 넣는 ArrayList의 index를 이용하여

이전 배열 index의 가격차이를 계산하여 가격 표시를 구현하였다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@SuppressLint({"ResourceAsColor""SetTextI18n"})
        public void onBind(OilPriceInfo.OilPrice oilAvg, int pos){
 
            day.setText(convertDateString(oilAvg.getDATE()));
            price.setText(Integer.toString( (int) oilAvg.getPRICE()));
 
            if(pos == oilAvg_List.size() - 1){
                priceGap.setText("-");
                priceGap.setTextColor(ContextCompat.getColor(itemView.getContext(),R.color.gray));
            }//7일전인 경우 이전 날짜 데이터가 없으므로 차이를 0으로 설정
            else{
 
                int gap = priceGap(pos);
 
                String priceText = Integer.toString(gap);
 
                if(gap > 0){
                    priceGap.setTextColor(ContextCompat.getColor(itemView.getContext(),R.color.orange));
                    priceGap.setText("+"+ priceText);
                }else if(gap < 0){
                    priceGap.setTextColor(ContextCompat.getColor(itemView.getContext(),R.color.purple_700));
                    priceGap.setText(priceText);
                }else{
                    priceGap.setTextColor(ContextCompat.getColor(itemView.getContext(),R.color.gray));
                    priceGap.setText(priceText);
                }
                
 
            }
 
            
        }
        
        public int priceGap(int pos){
 
            return (int) oilAvg_List.get(pos).getPRICE() -
                    (int) oilAvg_List.get(pos+1).getPRICE();
 
        }
cs

 

코드에서 보는 것과 같이 가격이 올라 간 경우와 내려간 경우 그리고

가격변화가 없는 경우의 가격차이 색깔을 다르게 하여 

가격차이를 더욱 선명하게 볼 수 있도록 하였다.