'개발/Android'에 해당되는 글 20건

  1. 2021.08.26 TextView 에 이미지 포함하기
  2. 2021.03.24 풍선도움말 레이아웃 구현 1
  3. 2021.03.05 프로젝트에 NDK 모듈 추가하기
  4. 2021.02.25 ViewBinding
  5. 2021.02.24 Android Studio Rename Package
  6. 2021.02.17 Appium 설치
  7. 2021.02.17 Appium 실행
  8. 2021.02.16 Android Screen Capture(화면캡쳐)

텍스트와 이미지가 함께 구성하는 경우가 종종 발생한다. ImageView를 별도로 구성하지 않고 TextView의 속성을 이용하면 쉽게 처리할 수 있다.

 

아래는 layout XML 의 내용이다.

 

root layout 태그에 xmlns:app="http://schemas.android.com/apk/res-auto" 를 추가한다.

    <androidx.constraintlayout.widget.ConstraintLayout 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="match_parent">

보통 왼쪽 오른쪽에 추가하는 케이스가 많은데 오른쪽은   app:drawableEndCompat, 왼쪽은 app:drawableStartCompat 을 이용하면 된다. 그리고 텍스트와 이미지 사이 간격은  android:drawablePadding 으로 조절한다.

<TextView
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:fontFamily="@font/font_medium"
     android:includeFontPadding="false"
     android:text="더보기"
     android:textAppearance="@style/Text14White"
     app:drawableEndCompat="@drawable/icon_angle_greater"
     app:drawableStartCompat="@drawable/icon_info"
     android:drawablePadding="10dp"
/>

 

소스코드로 설정할 경우, TextView의 setCompoundDrawables(left, top, right, bottom) 메소드를 이용해서 drawable을 원하는 위치에 설정해주면 된다.

val iconInfo = ContextCompat.getDrawable(requireContext(), R.drawable.icon_info)
val iconBracket = ContextCompat.getDrawable(requireContext(), R.drawable.icon_angle_greater)
binding.tvPoint.setCompoundDrawables(icon, null, iconBracket, null)

'개발 > Android' 카테고리의 다른 글

풍선도움말 레이아웃 구현  (1) 2021.03.24
프로젝트에 NDK 모듈 추가하기  (0) 2021.03.05
ViewBinding  (0) 2021.02.25
Android Studio Rename Package  (0) 2021.02.24
Appium 설치  (0) 2021.02.17
Posted by Lumasca
,

위와 같은 뷰를 구현하려고 보니 기존에 나인패치 이미지를 이용하고 있었다. 

매번 이미지 요청하기도 그렇고 xml을 이용해 할 수 있는 방법을 찾아보았다.

 

먼저 우측하단 모서리를 제외한 코너에 라운드가 적용된 사각형 모양을 작성한다. (drawable/bg_banner.xml)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#462c94"></solid>

    <corners android:topLeftRadius="8dp" android:topRightRadius="8dp"
        android:bottomLeftRadius="8dp" android:bottomRightRadius="0dp" />

</shape>

 


다음은 사각형 아래에 붙는 꼬리 삼각형 모양을 작성한다. (drawable/bg_banner_tail.xml)

 

크기는 width, height

위치는 item 태그내부의 gravity

모양은 path 태그의 pathData

M x,y 는 (x,y)로 이동하여서 새로운 경로를 시작

Z 는 시작 경로지점으로 선을 그려서 경로를 닫음

 

위 속성을 적절히 수정해가면 삼각형 모양을 만들 수 있다.

 

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" >
<item
    android:width="8dp"
    android:height="8dp"
    android:gravity="bottom|left">
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="8dp"
        android:height="8dp"
        android:viewportWidth="8.0"
        android:viewportHeight="8.0">
        <path
            android:pathData="M 0,8 8,8 0,0z"
            android:fillColor="#462c94"/>
    </vector>
</item>
</layer-list>

 


이제 위에서 작성한 2개의 drawable을 이용해 레이아웃을 작성한다. (layout.xml )

관리 편의상 TextView와 ImageView를 하나의 Layout에 넣는다. 

 

 

<androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/layoutBanner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="visible"
            >

            <TextView
                android:id="@+id/txtBanner"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_banner"
                android:gravity="center"
                android:paddingStart="8dp"
                android:paddingTop="8dp"
                android:paddingEnd="8dp"
                android:paddingBottom="8dp"
                android:text="풍선도움말"
                android:textColor="#ffffff"
                />

            <ImageView
                android:id="@+id/txtBannerTail"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_banner_tail"
                app:layout_constraintBottom_toBottomOf="@+id/txtBanner"
                app:layout_constraintLeft_toRightOf="@+id/txtBanner" />
        </androidx.constraintlayout.widget.ConstraintLayout>

'개발 > Android' 카테고리의 다른 글

TextView 에 이미지 포함하기  (0) 2021.08.26
프로젝트에 NDK 모듈 추가하기  (0) 2021.03.05
ViewBinding  (0) 2021.02.25
Android Studio Rename Package  (0) 2021.02.24
Appium 설치  (0) 2021.02.17
Posted by Lumasca
,

NDK는 아래 링크에서 다운로드해서 설치하면 된다.

 

developer.android.com/ndk/downloads

 

NDK 다운로드  |  Android NDK  |  Android Developers

개발 플랫폼에 맞는 NDK 패키지를 선택합니다. NDK 최신 버전 및 이전 버전의 변경사항에 관한 정보는 NDK 버전 기록을 참조하세요. macOS용 공증된 NDK가 필요하다면 ZIP 파일이 아닌 App Bundle을 다운

developer.android.com

프로젝트의 local.properties에 경로를 설정해주면 된다. 

 

ndk.dir=/Users/계정/Library/Android/sdk/ndk
sdk.dir=/Users/계정/Library/Android/sdk

 

NDK 모듈은 보통 c++로 작성된 라이브러리를 사용하거나 보안을 위해서 사용하곤 한다.

JNI(Java Native Interface)는 자바기반의 클래스에서 Native 코드에 접근하기 위한 인터페이스이다.

 

Java <-> JNI <-> C/C++


이제 NDK 모듈을 추가해보자.

 

스튜디오의 File -> New -> New Module ... 메뉴를 선택한다.

 

 

모듈 타입을 정하는 화면이 나오는데 Android Library 를 선택하고 Next 버튼을 클릭한다.

모듈명, 패키지명을 적절히 입력하고 Finish를 클릭하면 모듈이 생성된다.

 

모듈의 main 폴더를 우클릭하고 신규 디렉토리를 생성한다.

 

새 디렉토리 이름은 jni를 입력하고 Enter 키를 누른다. 보통 jni 폴더를 생성해서 cpp나 mk 파일을 그 폴더 내부에 넣는다.

 

이제 make 파일을 생성하자.

jni 폴더를 우클릭하고 새 파일을 생성한다. 

 

 

 

Android.mk 내용

#소스파일 위치: 현재 디렉터리
LOCAL_PATH := $(call my-dir)

#동일한 변수가 중복 사용되는 것을 방지
include $(CLEAR_VARS)

#모듈 이름
LOCAL_MODULE    := crypto
#소스파일 목록(c, cpp)
LOCAL_SRC_FILES := crypto.cpp

#LOCAL_CPPFLAGS := -fexceptions
#LOCAL_DEFAULT_CPP_EXTENSION := cpp
#LOCAL_LDLIBS += -llog

#빌드적용. 이게 정의 안되면 빌드할 모듈이 없다고 함
include $(BUILD_SHARED_LIBRARY)

아래 파일은 컴파일시 추가적으로 설정이 필요할때 사용한다. 없어도 무관..

Application.mk

#NDK 프로젝트 위치
#APP_PROJECT_PATH
#모듈이름
#APP_MODULES

#debug, release(기본)
APP_OPTIM := debug

#C소스 컴파일 플래그
#APP_CFLAGS
#C++소스 컴파일 플래그
#APP_CXXFLAGS
#C/C++ 컴파일 플래그
APP_CPPFLAGS := -frtti -fexceptions

#지원할 ABI, 모두는 all
APP_ABI := arm64-v8a armeabi-v7a

#사용할 library
APP_STL := c++_static

#APP_PLATFORM := android-29
NDK_TOOLCHAIN_VERSION := clang

#특정 Android.mk를 지정. 기본은 jni 밑에 Android.mk 파일 사용.
#APP_BUID_SCRIPT

 

자바 클래스를 생성한다.

public class Crypto {

	//crypto.cpp 에 정의할 함수, native 키워드가 붙는다.
    public static native byte[] getKey();

    static {
    //라이브러리 로딩, LOCAL_MODULE에 작성한 이름(so 파일명 앞에 lib만 제거된 이름과 동일)
        System.loadLibrary("crypto");
    }
}

 

이제 c/c++ 함수를 작성할 소스파일( cpp) 파일을 생성한다.

jni 폴더를 우클릭하고 C/C++ Source File을 클릭한다.

 

cpp 소스파일에 테스트할 코드를 간단히 작성한다.

 

함수명 작성 규칙은 JNIEXPORT $리턴타입 JNICALL Java_$패키지명_$클래스명_$함수명 이다.

리턴타입은 JNI 타입인데 자바에서 사용하는 데이터 타입 앞에 j를 붙인다.

byte -> jbyte

배열은 뒤에 Array를 붙인다. jbyteArray

 

패키지명은 현재 NDK 모듈의 패키지명이다. 

클래스명은 위에서 생성한 자바 클래스 명이다. 함수명은 자바 클래스에 정의된 함수명이다.

자바클래스와 cpp 함수간에 규약이 맞지 않으면 오류로 표시된다.

#include <jni.h>
  
  const char key[] = {
		0x10, 0x12, 0x49, 0x51,
		0x55, 0x48, 0x76, 0x5F,
		0x65, 0x6A, 0x6C, 0x33,
		0x6E, 0x6K, 0x77, 0x44
	};
    
  extern "C"
  
  JNIEXPORT jbyteArray JNICALL Java_com_test_cryptolib_Crypto_getKey(
		JNIEnv* env, jobject){
    jbyteArray array = env->NewByteArray(16);

    jbyte *bytes = env->GetByteArrayElements(array, 0);
    for(int i =0; i < sizeof(key); i++){
        bytes[i] = key[i];
    }
    env->SetByteArrayRegion(array, 0, 16, bytes);

    env->ReleaseByteArrayElements(array, bytes, 0);

    return array;
  }

$cd src/main

이 폴더에서 빌드하지 않으면 Your APP_BUILD_SCRIPT points to an unknown file: 오류가 발생한다.

 

이제 빌드를 하면...

$ndk-build

 

src/main/libs 폴더에 APP_ABI 에 정의한 이름( arm64-v8a, armeabi-v7a) 으로 폴더별로 so 파일이 생성된다.

 

 

생성된 so 파일들은 사용할 프로젝트의 src/main/jniLibs/ 폴더에 복사한다. 폴더구조는 그대로 유지한다.

src/main/jniLibs/arm64-v8a, src/main/jniLibs/armeabi-v7a 

 

이제 라이브러리를 사용할 자바 클래스를 생성한다. 패키지 경로는 NDK 모듈의 경로를 그대로 사용해야 한다. 

위에서 생성한 자바 클래스를 패키지 경로 그대로 복사하도록 한다.

 

 

사용할때는 Crypto.getKey(); 처럼 호출하면 된다.

 

'개발 > Android' 카테고리의 다른 글

TextView 에 이미지 포함하기  (0) 2021.08.26
풍선도움말 레이아웃 구현  (1) 2021.03.24
ViewBinding  (0) 2021.02.25
Android Studio Rename Package  (0) 2021.02.24
Appium 설치  (0) 2021.02.17
Posted by Lumasca
,

ViewBinding

개발/Android 2021. 2. 25. 10:26

app모듈 build.gradle 파일에 viewBinding 사용 설정을 한다.

 

android {

    buildFeatures{
        viewBinding = true
    }
}

 

Binding 클래스이름은 레이아웃 xml 파일명을 파스칼케이스(단어 첫글자를 대문자표기)로 변환하여 Binding을 붙인다.

 

예)

activity_main.xml => ActivityMainBinding

fragment_menu.xml => FragmentMenuBinding

contact_list_item.xml => ContactListItemBinding

 

Activity

//바인딩 변수 생성할때 초기화
val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
 
 	//기존
    //setContentView(R.layout.activity_main) 
    
    //변경 binding.root로 전달
	setContentView(binding.root)
}

fun initView(){
	binding.txtName.text = "Name" //사용은 기존코드에 binding. 만 앞에 추가하면 된다.
}

 

Fragment

//바인딩 변수
private lateinit var binding:FragmentMenuBinding

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

    //기존
    //val view = inflater.inflate(R.layout.fragment_menu, container, false)		   
    //return view
    
    //변경
    binding = FragmentMenuBinding.inflate(inflater, container, false)
    return binding.root
}

fun initEvent(){
    binding.btnUp.setOnclickListener{v:View? -> //TODO }
}

 

Adapter

class ContactListAdapter(val contacts: ArrayList<Contact>) : RecyclerView.Adapter<ContactListAdapter.ViewHolder>() {

	//기존
    //class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
    //변경 binding.root로 전달
    class ViewHolder(val binding: ContactListItemBinding) : RecyclerView.ViewHolder(binding.root){

        fun bindItems(contact:Contact){
        	//기존
            //val tvName = itemView.findViewById(R.id.tvName) as TextView
            //val tvNumber = itemView.findViewById(R.id.tvNumber) as TextView
            
            //변경 binding 추가
            binding.tvName.text = "("+ contact.id +") "+ contact.name
            binding.tvNumber.text = contact.number
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactListAdapter.ViewHolder {

		//기존
		//val v = LayoutInflater.from(parent.context).inflate(R.layout.contact_list_item, parent, false)
		//return ViewHolder(v)

		//변경
		val binding = ContactListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bindItems(contacts[position])
    }

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

'개발 > Android' 카테고리의 다른 글

풍선도움말 레이아웃 구현  (1) 2021.03.24
프로젝트에 NDK 모듈 추가하기  (0) 2021.03.05
Android Studio Rename Package  (0) 2021.02.24
Appium 설치  (0) 2021.02.17
Appium 실행  (0) 2021.02.17
Posted by Lumasca
,

 

Studio의 Project view에서 Android 로 변경한다.

 

옵션메뉴(톱니바퀴모양) 버튼을 누르고, Compact Middle Packages 에 체크가 해제되었는지 확인한다. 체크되었으면 클릭해서 선택해제한다.  이 옵션이 체크되어 있으면 중간 패키지명을 변경할 수 없다.

그러면 아래와같이 패키지 경로를 계층적으로 표시한다.

변경하고자 하는 계층에서 마우스를 우클릭해서 Refactor 메뉴로 이동한다.

Reactor 서브메뉴에서 Rename을 선택한다.

 

그러면 아래와 같은 경고창이 팝업된다.

Rename package를 클릭한다. Rename directory는 디렉터리 이름만 변경하는 기능이다.

 

변경하고싶은 이름을 입력하고 Refactor 버튼을 클릭한다.

 

상황에 따라서 Studio 하단에 Find Refactoring Preview 창이 노출될 수 있는데 'Do Refactor' 버튼을 클릭하면 되겠다.

 

마지막으로 app 모듈의 build.gradle 파일의 applicationId를 수정하면 끝이다.

 

 

'개발 > Android' 카테고리의 다른 글

프로젝트에 NDK 모듈 추가하기  (0) 2021.03.05
ViewBinding  (0) 2021.02.25
Appium 설치  (0) 2021.02.17
Appium 실행  (0) 2021.02.17
Android Screen Capture(화면캡쳐)  (0) 2021.02.16
Posted by Lumasca
,

Appium 설치

개발/Android 2021. 2. 17. 16:46

터미널에서 npm 을 이용해서 설치를 진행한다.

 

$npm install -g appium

 

Desktop용 앱은 아래 사이트에서 dmg 파일을 받아서 설치하면 된다.

github.com/appium/appium-desktop/releases

 

Releases · appium/appium-desktop

Appium Server and Inspector in Desktop GUIs for Mac, Windows, and Linux - appium/appium-desktop

github.com

appium-doctor는 Appium의 의존성을 검증하는 도구이다.

$npm install -g appium-doctor

 

의존성이 올바른지 아래와 같이 점검하면 된다.

$appium-doctor --ios

$appium-doctor --android

 

flutter Driver 설치

$npm i -g appium-flutter-driver

 

Appium 실행

lumasca.tistory.com/966

 

Appium 실행

여기서 Appium 설치는 다루지 않는다. 일단 터미널에서 로그인한 계정의 쉘 환경설정을 검토해보자. JAVA_HOME이 잘못설정되어 있어서 오류가 발생했었다. ANDROID_HOME 에는 설치된 Android SDK 경로인데

lumasca.tistory.com

 

'개발 > Android' 카테고리의 다른 글

ViewBinding  (0) 2021.02.25
Android Studio Rename Package  (0) 2021.02.24
Appium 실행  (0) 2021.02.17
Android Screen Capture(화면캡쳐)  (0) 2021.02.16
Flutter Studio  (0) 2021.02.03
Posted by Lumasca
,

Appium 실행

개발/Android 2021. 2. 17. 11:14

여기서 Appium 설치는 다루지 않는다. 설치는 아래링크에서 확인하면 된다.

lumasca.tistory.com/967

 

Appium 설치

터미널에서 npm 을 이용해서 설치를 진행한다. $npm install -g appium Desktop용 앱은 아래 사이트에서 dmg 파일을 받아서 설치하면 된다. github.com/appium/appium-desktop/releases Releases · appium/appium..

lumasca.tistory.com

 

일단 터미널에서 로그인한 계정의 쉘 환경설정을 검토해보자. JAVA_HOME이 잘못설정되어 있어서 오류가 발생했었다.

 

ANDROID_HOME 에는 설치된 Android SDK 경로인데 아마 개발환경이 구축되어 있다면 이미 되어 있을 것이다.

JAVA_HOME 경로가 설정안되어 있으면 추가해야 한다. JAVA_HOME은 터미널에서 /usr/libexec/java_home -V 명령을 실행하면 확인할 수 있다.

 

$cd ~

$vi .bash_profile

export ANDROID_HOME=/Users/kbcard/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Hom

수정한 환경설정을 적용한다.

$source .bash_profile

 

이제 Appium을 시작한다.

이제 첫 화면에서 Start Server 버튼을 클릭해서 서버를 시작하도록 하자.

Appium 서버가 실행중인 모습이다.

이제 돋보기 모양 버튼을 클릭하면 세션을 구성하는 화면이 보인다.

JSON 우측하단에 보면 Representation 항목이 있는데 안드로이드나 아이폰 테스트 환경에 맞게 구성해주면 된다.

 

아래는 안드로이드 예제이다.

{
  "platformName": "Android",
  "platformVersion": "9",
  "automationName": "Appium",
  "app": "/Users/$계정/Downloads/app-debug.apk",
  "appPackage": "com.example.flutter_app",
  "appActivity": "com.example.flutter_app.MainActivity"
}

automationName: 사용할 자동화 엔진. Appium이 기본, Android용은 UiAutomator2, Espresso, UiAutomator1 가 있고, 아이폰용은 XCUITest, Instruments 가 있다.

platformName: 사용할 모바일 OS 플랫폼. iOS, Android

platformVersion: 모바일 OS 버전.

deviceName: 사용할 모바일 디바이스나 에뮬레이터 종류. iPhone Simulator, Android Emulator 등. 안드로이드에서는 무시된다.

app: 디바이스에 설치할 apk 나 ipa 파일의 로컬 절대 경로 또는 원격 http URL

appActivity: 실행할 Activity명

appPackage: 실행할 앱의 패키지명

 

사용가능한 Capability 목록은 다음 링크에서 확인할 수 있다.

github.com/appium/appium/blob/master/docs/en/writing-running-appium/caps.md

 

이제 Start Session 버튼을 클릭한다. 이 작업은 시간이 조금 소요된다. 작업로그는 Appium 앱에서 확인할 수 있다.

 

문제가 없이 실행된다면 아래와 같은 화면을 보게될 것이다. 실제 디바이스 화면에서 실행된 앱이 보인다.

 

왼쪽화면에서 버튼을 눌렀더니 우측 Selected Element에 해당 버튼에 대한 속성들이 나타난다.

'개발 > Android' 카테고리의 다른 글

Android Studio Rename Package  (0) 2021.02.24
Appium 설치  (0) 2021.02.17
Android Screen Capture(화면캡쳐)  (0) 2021.02.16
Flutter Studio  (0) 2021.02.03
안드로이드 TextView 특수문자 표시  (0) 2021.02.01
Posted by Lumasca
,

스마트폰의 물리 전원버튼 + 볼륨 Down 버튼을 동시에 누르면 캡쳐된 이미지가 갤러리에 저장된다.

 

최근에 출시된 갤럭시 모델들은 손날로 화면을 쓸어주면(왼쪽->오른쪽 or 오른쪽 -> 왼쪽) 캡쳐가 된다.

 

Android Studio 에서는 Logcat Viewer 왼쪽에 Screen Capture(카메라 모양) 버튼이 있다. 이 버튼을 클릭하면 캡쳐한 이미지를 저장할 수 있다. 이 버튼이 비활성화된 경우는 디바이스가 제대로 연결이 안된 것이니 다시 확인하도록 한다.

 

혹시 Logcat Viewer가 안보이면 메뉴에서 View -> Tool Windows -> Logcat을 선택하면 스튜디오 하단에 노출된다.

 

'개발 > Android' 카테고리의 다른 글

Appium 설치  (0) 2021.02.17
Appium 실행  (0) 2021.02.17
Flutter Studio  (0) 2021.02.03
안드로이드 TextView 특수문자 표시  (0) 2021.02.01
onTouch lambda should call View#performClick when a click is detected  (0) 2021.01.21
Posted by Lumasca
,