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

매번 이미지 요청하기도 그렇고 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
,

사진 편집 프로그램인 PhotoScape가 필요하다. 다운로드 하러 간다.

 

x.photoscape.org/

 

PhotoScape X for Mac and Windows 10

History There is a mouse issue in macOS ( Mojave 10.14.0 ~ 10.14.3 and Catalina 10.15 ). A fix for this will be available shortly. --> * Version 4.1.1 (Nov 6, 2020) * Version 4.1 (Oct 22, 2020) (PhotoScape X 4.1 for Windows 10 will be released very soon) -

x.photoscape.org

 

오른쪽의 MAC 다운로드를 클릭한다.

 

App Store 열기를 클릭한다.

 

 

앱이 설치된 상태여서 OPEN 버튼으로 되어 있다.

설치가 안된 상태라면 GET으로 되어 있다. 버튼을 클릭하면 INSTALL로 바뀐다. 클릭해서 설치한다.

설치가 완료되면 OPEN 버튼을 클릭해서 실행한다.

 

상단 메뉴탭에서 사진편집 으로 이동한다.

그리고 편집할 사진이 있는 폴더를 선택한다.

그러면 해당폴더가 팝업되면서 아래와 같은 메시지가 노출될 것이다. 허용 버튼을 클릭한다.

 

그럼 아래 처럼 폴더내 이미지 파일을 볼수 있다.

모자이크를 적용할 사진을 선택한다.

 

오른쪽 편집메뉴에서 도구 버튼을 클릭하면 아래쪽에 모자이크 도구가 보인다. 클릭한다.

 

이미지에서 정보를 식별불가하게 만들것이므로 브러시 크기만 조절한다.  슬라이더로 좌우로 움직여서 적절한 크기를 설정한다.

 

마우스로 모자이크 처리할 영역을 드래그하면 뭉개지게 된다.

 

이제 저장하도록 한다. 우측 하단에 저장버튼이 있다. 클릭한다.

 

저장 팝업이 나타나는데 적절히 옵션을 설정하고 저장 버튼을 눌러서 저장하면 된다.

 

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
,

IntelliJ Community Edition 버전은 Spring Boot를 지원하지 않는다.

 

그래서 아래 사이트에서 프로젝트를 생성해서 이용하도록 한다. 여기서 생성한 프로젝트를 IntelliJ에서 오픈하면 폴더구조에 맞게 Application 클래스, 테스트 클래스, build.gradle 파일 등이 구성되어 있다.

 

start.spring.io/

 

위와 같은 화면이 나오는데 왼쪽은 프로젝트 정보를 작성하는 영역이고, 오른쪽은 프로젝트 디펜던시를 추가하는 부분이다.

원하는 대로 설정하고 하단에 GENERATE 버튼을 누르면 압축파일을 다운로드한다.

 

다운 받은 파일을  압축을 풀고 IntelliJ에서 오픈하면 된다.

 

IntelliJ에서 Run/Debug Configuration 을 설정해줘야 한다.

빌드(망치모양) 버튼 옆에 Edit Configuration 을 선택한다.

아래 창이 나타나는데 추가(+)버튼을 클릭하고, Application을 선택한다.

Main class 항목에 Application 클래스를 설정해준다. 우측에 ... 버튼을 클릭하면 Application 클래스가 보일 것이다.

혹시 찾이 못하면

혹시 Application 클래스를 자동으로 찾지 못하면 중간에 Use classpath of module 항목이 있는데 Application클래스가 위치한 경로를 선택해주면 된다.

 

이제 Application을 실행하면 된다.

 

Application Class 설정이 잘못되면 실행할때 아래 오류가 발생할 수 있다.

java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication

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

IntelliJ LomBok Plugin 설치  (0) 2020.08.07
MariaDB(mysql) root password 분실 초기화  (0) 2020.08.07
gradle wrapper version 변경  (0) 2020.03.11
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
,