안드로이드 UI를 개발할때 Activity 기반에서 Fragment기반으로 개발한지도 상당한 시간이 흘렀습니다..

지금까진 Fragment를 Activity에 붙이기 위해선 FragmentManager 를 이용하여 replace 또는 add를 해야만 했습니다.

예를 들면 아래와 같습니다.

FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragment_id, CustomFragment(), "tag_name");
ft.commitAllowingStateLoss();
fm.executePendingTransactions();

시간이 흐르면서 안드로이드도 많은 발전을 했는데, 그중 하나가 NavHostFragment를 통한 네비게이션 입니다.

 

안드로이드 스튜디오를 통해서 NavHostFragment 코드를 보면 Fragment를 상속하고 있고 NavHost 인터페이스를 구현하고 있습니다.

NavHost 는 내부적으로 아래와 같은 인터페이스 함수를 가지고 있습니다.

public interface NavHost {

    /**
     * Returns the {@link NavController navigation controller} for this navigation host.
     *
     * @return this host's navigation controller
     */
    @NonNull
    NavController getNavController();
}

그럼 실전에서 어떻게 구현이 가능한지 확인해 보겠습니다.

자세한 사항은 아래 링크를 통해서 확인하시면 됩니다.

developer.android.com/guide/navigation/navigation-getting-started?hl=ko

 

1. xml을 통하여 fragment 또는 FragmentContainerView 선언

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />
</FrameLayout>

fragment와 FragmentContainerView 의 차이는 z-ordering를 처리할수 있느냐에 있습니다.

자세한 내용은 아래 링크를 통해서 확인하시면 됩니다.

developer.android.com/reference/androidx/fragment/app/FragmentContainerView

 

FragmentContainerView  |  Android 개발자  |  Android Developers

From class android.view.ViewGroup void addChildrenForAccessibility(ArrayList arg0) void addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo arg0, String arg1, Bundle arg2) void addFocusables(ArrayList arg0, int arg1, int arg2) void addKeyboardNavigat

developer.android.com

2. FragmentContainerView 에서 선언한 nav_graph 작성

<?xml version="1.0" encoding="utf-8"?>
<navigation android:id="@+id/nav_graph"
    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"
    app:startDestination="@+id/nav_xxx_start">

    <fragment
        android:id="@+id/nav_xxx_start"
        android:name="com.aaa.bbb.ccc.CustomFragment"
        android:label="main fragment"
        tools:layout="@layout/fragment_start_layout" />

    <fragment
        android:id="@+id/nav_webview"
        android:name="com.aaa.bbb.ccc..WebViewFragment"
        android:label="webview fragment"
        tools:layout="@layout/fragment_webview" />
</navigation>

위에서 보면 navigation tag의 app:startDestination 속성이 있는데 이걸 처음 실행할 fragment id로 설정해 줍니다.

각 fragment에는 name속성에 : Fragment 이름, layout속성에는 해당 프레그먼트의 layout id를 넣어줍니다.

 

3. 실제 네비게이션

findNavController(R.id.nav_host_fragment).navigate(R.id.nav_xxx_start)

findNavController의 id는 NavHostFragment 의 id를 사용하고 뒤의 navigate에서는 실제로 움직일 프레그먼트의 id를 넣어줍니다.

만약 argument를 같이 넣어줄 필요가 있으면 아래와 같이 Bundle을 추가해 줍니다.

val bundle = Bundle().apply {
	putInt(EXTRA_TYPE, position)
}

findNavController(R.id.nav_host_fragment).navigate(R.id.id_want_to_go, bundle)

findNavController 를 사용하면 기본적으로 fragment가 중복 적재됩니다. 다시 말하면 같은 프레그먼트를 호출하면 같은 프레그먼트가 적재되고, backkey를 통해서 하나씩 빠지게 됩니다. 만약 이런 처리를 방어하기 위해서는 아래와 같이 navigation option 처리를 하면 됩니다.

val bundle = Bundle().apply {
	putInt(EXTRA_TYPE, position)
}
val navOption = NavOptions.Builder().setLaunchSingleTop(true).build()
findNavController(R.id.nav_host_fragment).navigate(R.id.id_want_to_go, bundle, navOption)

 잘만 활용하면 기존 방식보다 훨씬 짧은 코드로 쉽게 앱개발을 할수 있을것 같습니다.

'프로그래밍 > 안드로이드' 카테고리의 다른 글

app-ads.txt 처리하는 방법  (0) 2020.02.13
Custom view cycle.  (0) 2016.12.14
Stop watch  (0) 2016.11.29
안드로이드 광고 모듈  (0) 2016.11.25
Glide에서 디스크 캐쉬 사용하지 않게 하는 방법  (0) 2016.09.28
by Invincible Cooler 2020. 12. 1. 11:38
class CircleColorImageView : AppCompatImageView {

    private lateinit var paint: Paint

    constructor(context: Context) : super(context) {
        init()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        init()
    }

    private fun init() {
        paint = Paint(Paint.ANTI_ALIAS_FLAG)
        paint.run {
            style = Paint.Style.FILL
            color = Color.TRANSPARENT
        }
    }

    fun setColor(resId: Int) {
        paint.run {
            color = resId
        }
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        val x = canvas?.width ?: 0
        val y = canvas?.height ?: 0

        if (x == 0 || y == 0) {
            return
        }

        val half = (x / 2).toFloat() // 정확한 원이기 때문에 x/2, y/2, radius 값이 같아서 이렇게 처리해도 무방함

        canvas?.drawCircle(half, half, half, paint)
    }
}
by Invincible Cooler 2020. 3. 6. 17:16

안녕하세요. 오늘은 app-ads.txt를 처리하는 방법에 대해서 적어 볼까 합니다.

 

개인 개발자 기준이며, Website를 운영하는 단체 및 회사와는 관련이 없습니다.

 

- 접속 : admob.com

1. 좌측 메뉴의 앱 -> 모든 앱 보기 -> APP-ADS.TXT 탭 누름 

이렇게 하면 상태가 회색이고 app-ads.txt URL에 아무것도 안나옵니다.

 

그러면 약간 오른쪽의 APP-ADS.TXT설정 방법을 눌러서 설정방법을 참고 합니다.

개인 개발자들은 보통 개인 Website를 구축하고 있지 않기 때문에 

https://www.app-ads-txt.com/ 의 도움을 받아 처리하면 됩니다. 로그인을 하고 하라는 대로 하면

 

오른쪽 Copy to clipboard위에 https url이 나옵니다.

그러면 이 url을 복사해서 구글 콘솔의 해당앱 선택 좌측 메뉴의 앱정보 -> 스토어 등록정보 -> 연락처 세부정보의 웹사이트에 해당 url을 넣어주시면 1~2일 후 admob에 가보면 녹색으로 변화된걸 볼 수 있을겁니다.

 

이런건 티스토리나 Naver블로그에서 지원해 주면 좋은데 아직은 안해주네요.

 

by Invincible Cooler 2020. 2. 13. 09:53

Invincible의 RxKotlin

RxKotlin은 RxJava, RxAndroid에 비해서 참고 자료가 부족하네요...

 

요즘 한창 RxJava를 공부하고 있는데, 공부하면서 정리도 할겸 공유도 할겸 블로그 글을 써볼까 합니다.

 

Rx(Reactive) 시리즈는 java 뿐만 아니라 다른 언어에서도 많이 사용합니다. 전 안드로이드에서 사용하기 위해서 RxKotlin을 채택하고 연습하기로 했습니다. 먼저 제가 공부를 하고 있는 책은 "리액티브 프로그래밍 기초부터 RxAndroid까지 한번에 RxJava프로그래밍" 저자는 유동환, 박정준이라고 되어 있네요.

Rx프로그래밍은 RxJava나 RxAndroid나 RxKotlin이나 비슷한것 같네요.

 

책에 있는 예제를 RxKotlin을 사용해서 프로그래밍 해봤습니다. 처음이라 그런지 한참 헤맸습니다.

RxKotlin을 사용하기 위해서는

1. build.gradle에 아래와 같이 추가 해야 합니다.

implementation("io.reactivex.rxjava2:rxkotlin:2.4.0")
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"

 

RxJava를 기반으로 RxAndroid가 나오고 이걸 기반으로 RxKotlin이 나오기 때문에 버전은 약간씩 다르네요. 가장 최신기술은 RxJava에 있겠네요.

 

제가 하려고 하는 작업은 

안드로이드의 설치된 앱정보를 가져와 RecyclerView에 나오게 하려고 합니다.

 

먼저 xml 입니다.

1. recycler_test_activity.xml 입니다.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />
</RelativeLayout>

2. 아답터에서 사용할 xml 모습입니다.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/thumb_iv"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp" />

    <TextView
        android:id="@+id/text_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        android:layout_toRightOf="@+id/thumb_iv" />
</RelativeLayout>

 

XML은 전부 완성된것 같으니 Activity클래스의 코드를 작성할 차례네요.

 

package com.example.rxtest

import android.content.Context
import android.content.Intent
import android.content.pm.ResolveInfo
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.util.*
import kotlin.collections.ArrayList

class RecyclerTestActivity : AppCompatActivity() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var context: Context

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.recycler_test_activity)

        context = this

        recyclerView = findViewById(R.id.recycler_view)
        recyclerView.apply {
            layoutManager = LinearLayoutManager(context)
            adapter = TestMenuListAdapter()
            setHasFixedSize(true)
        }

        val disposable = getItemObservable()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe {
                (recyclerView.adapter as TestMenuListAdapter).run {
                    setItems(it)
                }
            }
    }

//    private fun getItemObservable(): Observable<RecyclerItem> {
//        val pm = packageManager
//        val i = Intent(Intent.ACTION_MAIN).run {
//            addCategory(Intent.CATEGORY_LAUNCHER)
//        }
//
//        return pm.queryIntentActivities(i, 0).toObservable()
//            .sorted(ResolveInfo.DisplayNameComparator(pm))
//            .subscribeOn(Schedulers.io())
//            .observeOn(Schedulers.io())
////            .doOnNext {
////                Log.d("AAA", "title : ${it.activityInfo.loadLabel(pm)}")
////            }
//            .map { item ->
//                val image = item.activityInfo.loadIcon(pm)
//                val title = item.activityInfo.loadLabel(pm).toString()
//                return@map RecyclerItem(image, title)
//            }
//    }

    private fun getItemObservable(): Observable<MutableList<RecyclerItem>> {
        val pm = packageManager
        val i = Intent(Intent.ACTION_MAIN).run {
            addCategory(Intent.CATEGORY_LAUNCHER)
        }

        return Observable.fromArray(pm.queryIntentActivities(i, 0))
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .map { unSortedList ->
                val sortedList = ArrayList(unSortedList)
                Collections.sort(sortedList, ResolveInfo.DisplayNameComparator(pm))
                return@map sortedList
            }
            .flatMap { sortedList ->
                Observable.fromIterable(sortedList)
                    .map { item ->
                        val image = item.activityInfo.loadIcon(pm)
                        val title = item.activityInfo.loadLabel(pm).toString()
                        RecyclerItem(image, title)
                    }
                    .toList()
                    .toObservable()
            }
    }

    private inner class TestMenuListAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
        private val VIEW_TYPE_ITEM = 1

        private var mItems = ArrayList<RecyclerItem>()

        fun setItems(items: MutableList<RecyclerItem>) {
            mItems.addAll(items)
            notifyDataSetChanged()
        }

        fun setItems(items: RecyclerItem) {
            mItems.add(items)
            notifyDataSetChanged()
        }


        override fun getItemCount(): Int {
            return mItems?.size
        }

        override fun getItemViewType(position: Int): Int {
            return VIEW_TYPE_ITEM
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            return ItemViewHolder(LayoutInflater.from(context).inflate(R.layout.app_item, parent, false))
        }

        override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
            when (viewHolder.itemViewType) {
                VIEW_TYPE_ITEM -> {
                    val vh = viewHolder as ItemViewHolder
                    val info = mItems?.get(position)
                    vh.textTv.text = info.title
                }
            }
        }

        private inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
            val thumbIv = view.findViewById(R.id.thumb_iv) as ImageView
            val textTv = view.findViewById(R.id.text_tv) as TextView
        }
    }

    private data class RecyclerItem(val thumb: Drawable?, val title: String?)
}

 

먼저 

Observable<RecyclerItem> 를 리턴하는 getItemObservable() 함수를 보시면 안드로이드 앱에서 설치된 앱리스트를 가져와서 각 아이템을 RecyclerItem으로 변경해서 아답터에 데이터를 추가합니다.

이러면 각 아이템에 대해서 notifyDataSetChanged()를 해야하는데, 이러면 효율이 떨어집니다. 

그래서 각 아이템을 list에 담아서 notifyDataSetChanged()를 한번만 할 수 있게 Observable<MutableList<RecyclerItem>> 를 리턴하도록 변경하였습니다. 더 좋은 방법이 있을 것 같은데, 저도 아직은 Rx와 안친해서 잘 모르겠네요.

 

각 함수 (map, flatmap, toList 등)를 적절히 조합하는게 최대 난제 인것 같습니다. 그럼 익숙해지는 그날까지 즐프요.

by Invincible Cooler 2020. 1. 15. 15:41

한국사 요점정리 앱은 개인정보를 수집하지 않습니다.

by Invincible Cooler 2018. 10. 4. 10:42

비트코인을 처음 시작할때 거래소는 무엇을 택해야 하는가?


세계1위의 빗썸 아니면 업비트? 결론부터 말하자면, 업비트를 강력추천한다.


빗썸은 아직 모바일 네이티브가 아닌, 모바일웹 화면으로 운영이 된다. 그에반해 업비트는 네이티브로 되어 있고, UI도 깔끔하다.


거래에서 가장 중요한게 내가 지금 수익률과 현재 거래가 어떻게 되고 있는가 인데, 가장기본이라고 생각했던 이런것조차 빗썸에서는 제공하지 않는다. 아무래도 빗썸은 인터페이스를 많이 바꿔야 할듯.


지금은 빗썸, 업비트를 포함한 모든 거래소에서 신규계정 발급을 막고 있어, 신규거래는 할수 없지만, 1월 20일 이후에는 가능하다고 하니, 왠만하면 업비트 사용을 강추드린다.


포탈을 검색해 보니, 빗썸은 출금도 바로바로 안해주는듯. 그에반해 업비트는 1분안에 모든게 완료. 못믿겠다면 직접 사용해 보시길.

'주식' 카테고리의 다른 글

미국의 금리 인상과 중국증시  (0) 2015.09.08
LG전자 그 세번째...  (0) 2015.07.30
LG전자 그 두번째 이야기.  (0) 2015.07.29
LG전자  (0) 2015.07.28
한국가스공사  (0) 2015.04.08
by Invincible Cooler 2018. 1. 1. 10:53

일렉트로 마법사.


이건 진짜 슈퍼셀에서 잘못 만든 유닛인듯.


인페르노 시리즈 (인페르노 드래곤, 인페르노 타워), 스파키 병신 만드는 캐릭터네.


일렉트로 마법사 없는 사람은 진짜, 재미가 훅 떨어짐. 그래서 아레나 9가면, 일부러 계속 져서 아레나 6까지 떨구고, 다시 9가고 반복중. 


안그럼 멘탈이 나갈지 모름.


스파키는 일렉트로 마법사 만나면 한대도 못때리고 죽음.


전설은 한번에 2개이상 장착 못하도록 만드는 패치가 필요할듯 보임. 그리고 인페르노 타워. 이거 쓰는 사람진짜... 게임은 이길수 있으나 3크라운 못하는 재미없는 게임이 됨.


그래서 그냥 안함. 무조건 3크라운 공격 스타일인 나는 재미가 없음. 1, 2 크라운으로 이기는건 중요하지 않음. 무조건 3크라운 도전.

by Invincible Cooler 2017. 6. 24. 20:39

스타크래프트 1.18에 대해서 써보려고 합니다.


1998년부터 스타를 했으니 이제 거의 20년이 되어 가네요. 청춘을 스타와 보냈는데, 1.18이 나와서 너무나 기뻤습니다. 하지만 로그인을 하는 순간 어~~~ 하는 느낌을 받게 되었죠. 이유는...


내가 원하는방 조인이 되지 않습니다. 방의 리프레쉬가 엄청 빠른타이밍에 일어나고, 클릭도 제대로 안됩니다. 랙이 너무 심하네요. 네트웤이 느린건 아니니, 프로그램 문제일 겁니다.


요즘 계속 업데이트가 일어나고 있는데, 쓰잘데기 없는 업데이트만 하고 있나봅니다. 사용자는 자기가 원하는방 조인을 하고 싶은 업데이트를 원하는데, 항상 체감에도 없는 업데이트만 하는건 한국 기업이나, 미국 기업이나 똑같은것 같군요.


제발 1.18도 원하는방 조인좀 할수 있게 바뀌었으면 좋겠습니다. 이러다 사용자들 다 그만 두는게 아닌가 걱정스럽네요. 스타를 사랑하는 한 사람으로서, 제발 바뀌었으면 좋겠네요. 이렇게라도 넉두리 남겨봅니다.


이 문제때문에 요즘은 잘 조인을 안하게 되네요.

by Invincible Cooler 2017. 5. 9. 18:28

super power flash light is required CAMERA permission. because it is using camera module.


by Invincible Cooler 2017. 2. 4. 07:51

안드로이드 개발시 가장 많이 사용하는 Custom view


이때 customview가 어떻게 작동하는지 cycle을 아는게 너무 중요함





by Invincible Cooler 2016. 12. 14. 17:31
| 1 2 3 4 ··· 7 |