반응형

어느날 APK를 생성하니 

 

아래와 같이 에러가 발생 되었습니다.

 

 

자세히 확인해 보니 jcenter()에서 api를 가져오지 못하는것으로 보입니다.

 

그래서

 

build.gradle의 jcenter() 호출하는 부분을 mavenCentral()로 변경 했더니 해결이 되었습니다.

 

As-Is 

문제가 발생된 코드

 

To-Be

해결된 코드

 

위의 To-Be 와 같이 하였을 때도 동일하게 오류가 발생 될경우

jcenter() 호출하는 부분을 하나하나 mavenCentral()로 바꿔 보면 해결되는지 확인하면 될듯 합니다.

반응형
반응형

 

아래와 같이 에러가 발생될 경우

 

Caused by: com.android.builder.errors.EvalIssueException: Configuration `:app:debugRuntimeClasspath` contains AndroidX dependencies, but the `android.useAndroidX` property is not enabled, which may cause runtime issues.

 

아래 파일들이 누락되어있는 경우 발생합니다.

gradle-wrapper.properties 파일

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

 

gradle.properties 파일(중요)

@@ -0,0 +1,24 @@
# Project-wide Gradle settings.

# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.

# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
반응형
반응형

 

특정 URL로 통신하니 

java.io.IOException: Cleartext HTTP traffic to test.tlshenm.com not permitted 오류가 발생되었습니다.

안드로이드 파이(9)부터 Http 통신시 발생된다고 하더군요...

해결방법

Https 통신으로 바꾸거나 바꿀수 없으면 허용을 해줘야 합니다.

저는 허용하는 방법을 안내하겠습니다.

 

1. res/xml폴더를 생성하시구요

2. res/xml/network_security_config.xml파일을 생성합니다.

3. 아래그림과 같이 예외처리할 URL(test.tlshenm.com)을 넣어 줍니다.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">localhost</domain>
        <domain includeSubdomains="true">test.tlshenm.com</domain>
    </domain-config>
</network-security-config>

마지막으로 AndroidManifast.xml안에 <application> 태그안에 아래 코드를 넣어 줍니다.

    <application
        android:networkSecurityConfig="@xml/network_security_config"
        . . . . . .>

이제 빌드해보시면 정상적으로 동작되실겁니다.

반응형
반응형

오랜만에 새로운 버전으로 빌드해보니 오류가 발생되었습니다.

Installed Build Tools revision 31.0.0 is corrupted. Remove and install again using the SDK Manager.

 

31 SDK를 설치해도 동일하게 발생되더군요.

 

해결방법

SDK를 30으로 낮춘다...

(나중에 31로 플레이스토어에 배포하라 그러면 어쩌라는 거냐...)

그림과 같이 바꾸시면 됩니다.

그럼 빌드 되실겁니다.

반응형
반응형

안드로이드 소스를 비교하는 방법에 대해 설명드리도록 하겠습니다.

 

 

우선 프로젝트를 먼저 실행해 줍니다.

 

저는 예시로 OldProject 와 NewProject를 소스를 비교할겁니다.

 

1. OldProject를 안드로이드 스튜디오로 실행 시켜 줍니다.

2. OldProject에서 아래와 그림과 같이 비교할 특정폴더(OldProject/app/main)를 선택합니다.

 

3. 마우스 오른쪽 클릭하여 메뉴를 엽니다.

4. 메뉴 중 Compare With를 선택합니다.

 

5. 비교할 프로젝트의 소스 폴더를 선택후 Open 버튼을 누릅니다.

예시)

OldProject/app/main

NewProject/app/main 선택

 

6. 짜잔~! OldProject/app/main과 NewProject/app/main의 소스파일들이 비교됩니다.

이런식으로

안드로이드 스튜디오에서 비교하면 어떤 소스가 추가 되었는지 또 어떤 소스가 없어졌는지 명확히 알수 있습니다.

 

반응형
반응형

안드로이드 백버튼 두번 누른 후 앱 종료동작


public class MainActivity extends Activity {
private Context mContext = null;
private long backKeyPressedTime = 0;


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mContext = this;
}

@Override
public void onBackPressed() {
if (System.currentTimeMillis() > backKeyPressedTime + 2000) {
backKeyPressedTime = System.currentTimeMillis();
Toast.makeText(this, "\'뒤로\'버튼을 한번 더 누르시면 종료됩니다.", Toast.LENGTH_SHORT).show();
return;
}
if (System.currentTimeMillis() <= backKeyPressedTime + 2000) {
super.onBackPressed();
}
}



반응형
반응형

아래와 같이 에러가 발생하였을때


ERROR: Unable to find method 'org.gradle.api.tasks.compile.CompileOptions.setBootClasspath(Ljava/lang/String;)V'.

Possible causes for this unexpected error include:

Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.)

Re-download dependencies and sync project (requires network)


The state of a Gradle build process (daemon) may be corrupt. Stopping all Gradle daemons may solve this problem.

Stop Gradle build processes (requires restart)


Your project may be using a third-party plugin which is not compatible with the other plugins in the project or the version of Gradle requested by the project.


In the case of corrupt Gradle processes, you can also try closing the IDE and then killing all Java processes.



해결방법


Project/gradle/wrapper/gradle-wrapper.properties 파일로 들어가서

5.1.1부분을 4.6이라던지 4.1로 버전을 변경해보시면 정상 빌드 되실겁니다.


그래도 안되시면 

안드로이드 스튜디오를 종료후 재시작 해보시거나

File/Sync Project with Gradle File를 눌러 보시기 발랍니다.




반응형
반응형

곧 안드로이드 스튜디오 build.gradle  compile 명령어가 Deprecated 된다고 합니다.

대신 implementation, api 명령어로 모듈을 import할 수 있습니다.



반응형
반응형

*요약 

- 이제 findViewById()를 쓸 필요가 없다


기존에는 xml에 id를 선언하고 해당 ui를 컨트롤 하기 위해서는 클래스에 findViewById()를 무조건 해줬습니다.

다음과 같이 말이죠


activity_main.xml

<android.support.constraint.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<TextView
android:id="@+id/tv_ShowText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/btn_GetData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="getDataButton" />

</android.support.constraint.ConstraintLayout>

MainActivity.java

private TextView mShowText = null;
private Button mGetBtn = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mShowText = (TextView) findViewById(R.id.tv_ShowText);
mGetBtn = (Button) findViewById(R.id.btn_GetData);
}

지금은 ui가 Text와 Btn밖에 없기 때문에 findViewById()를 2번만 선언하였습니다.

하지만 앱이 복잡해지고 ui 컴포넌트가 많아질경우 findViewById는 얼마나 많이 선언해줘야 할까요?



다음과 같이 개선된 방식을 사용하면 일일이 findViewById를 선언해 주지 않아도 됩니다.

1. 앱 단위 build.gradle에 dataBinding을 선언해 줍니다.

dataBinding {
enabled = true
}


2.layout xml 최상위 루트에 <layout> 태그를 넣어 줍니다.

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

<android.support.constraint.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<TextView
android:id="@+id/tv_ShowText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/btn_GetData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="getDataButton" />

</android.support.constraint.ConstraintLayout>
</layout>


3.클래스에서는 setContentView(R.layout.activity_main);-> DataBindingUtil.setContentView(this, R.layout.activity_main); 같이 변경해준다.

private ActivityMainBinding mBinding;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mBinding.tvShowText.setText("바뀐다");
}

*참고 ActivityMainBinding클래스는 실제로 없습니다. 안드로이드에서 자동적으로 만들어 줍니다.

그래서 ReBuild 하기 전까지 오류로 표시될것입니다.

하지만 따라가야할 규칙이 있습니다. 멤버변수 클래스 네임은 xml파일을 따라갑니다. 

activity_main.xml ->    ActivityMainBinding

activity_content.xml ->    ActivityContentBinding

activity_love.xml ->    ActivityLoveBinding

main_act.xml    ->    MainActBinding

love_act.xml    ->    LoveActBinding


*Fragment에서 사용법

fragment_blank.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android">

<FrameLayout xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.damoa.framenttest.BlankFragment">

<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />

<WebView
android:id="@+id/test_webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</FrameLayout>
</layout>

BlankFragment.java

private FragmentBlankBinding mBinding = null;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_blank, container, false);
mBinding.testWebview.loadUrl("https://www.google.co.kr");

return mBinding.getRoot();
}







반응형
반응형

태블릿을 개발하다보면 새로나오는 태블릿마다 해상도가 깨지는 현상이 발생한다.

DP로 정해줘도 태블릿이 새로나오면 UI가 깨지는 현상이 발생된다.

이유는

1. 해상도가 다르다.

2. DPI가 다르기 때문이다.


아래에 표를 보면 태블릿의 DP해상도는 크게 3개의 크기로 구분 할 수 있을거 같다.

1280 * 800

1024 * 768

960 * 600


*참고1) PX/DPI = DP , DP*DPI = PX

*참고2) smallestWidth(DP)는 가로,세로의 상관없이 가장 적은  비율의 값

*참고 표1 Orientation = LandScape 기준으로 작성

 기종

PX

DPI

  DP

smallestWidth(DP)  

WIDTH 

HEIGHT 

WIDTH 

 HEIGHT

Galaxy Note 10.1 2014 Edition

 2560

1600 

1280 

800 

 800

 Galaxy Note 10.1

 1280

800 

1280 

800 

800 

 Galaxy Tab 10.1

 1280

800 

1280 

800 

800 

 Galaxy Tab  8.9

1280 

800 

1280 

800 

800 

 Galaxy Tab A(2016)

1920 

1200 

1.5 

1280 

800 

800 

 Galaxy Tab S2

2048 

1536 

1024 

768 

768 

 Galaxy Tab A 9.7

1024 

768 

1024 

768 

768 

 Nexus 9 

2048 

1536 

1024 

768 

768 

 Galaxy Tab S3 

 2048

1536 

2.25 

910.2 

682.7 

682.7 

 Galaxy Tab E(W/B)

 1280

800 

1.33

961.5 

600.9 

600.9 

 G Pad3 8.0

 1920

1200 

960 

600 

600 


위에 표를 보았을 때 느낀점은 Galaxy Tab S3는 왜 DPI가 저럴까?;; 필자도 궁금하다.


태블릿마다 해상도가 깨지는 현상이 발생시 수정방법은 여러가지가 있겠지만 그중에 2가지를 들 수 있을거 같다.


방법 1.직접적으로 layout 폴더를 분기 처리한다.

장점

-부분적으로 고치기 딱좋다고 생각된다. UI가 중첩되거나 깨지는 부분만 고치면 되기 때문이다.

단점

-UI가 중첩되거나 깨지는 부분만 고치게 될 경우 UI가 언밸런스 해보일 수 있다.

 

참고 표2

 레이아웃 폴더 분기 종류

 설명

비고 

 layout

 기본레이아웃

 

layout-port

 가로모드시 레이아웃 적용

 

layout-land 

세로모드시 레이아웃 적용 

 

layout-sw600dp 

smallestWidth의 값이 600dp이상일 경우 적용

 1. smallestWidth의 값이 650dp이고 layout-sw600dp, layout-sw651dp폴더가 있을경우 smallestWidth 650dp의 값은 layout-sw600dp에 영향을 받는다.

2. 폴더명 뒤에 -port,-land는 적용 되지 않는다.

 layout-w600dp-port

세로모드의 넓이 dp값이 600이상일 경우 레이아웃 적용

세로모드의 넓이 dp값이 650이고layout-w600dp-port, layout-w651dp-port폴더가 있을경우 650dp의 값은 layout-w600dp-port에 영향을 받는다.

 layout-w600dp-land

가로모드의 넓이 dp값이 600이상일 경우 레이아웃 적용

가로모드의 넓이 dp값이 650이고 layout-w600dp-port, layout-w651dp-port폴더가  있을경우 650dp의 값은 layout-w600dp-port에 영향을 받는다.


방법 2.직접적으로 value폴더의 diman.xml를 분기 처리한다.

장점

-UI를 한꺼번에 고치기 딱 좋다. 깨지는 태블릿의 비율만 계산하여 폴더 분기 처리하면 한꺼번에 적용되기 때문이다.

단점

-비율계산이 잘못될 경우 기존 UI가 망가질수 있다.

참고 표3

-해상도 비율

 해상도

 비율

1280*800 

8:5 

1024*768 

4:3 

960*600 

8:5 









반응형
반응형

안드로이드 앱설치 여부를 확인하기 위해서는 해당앱의 패키지 명을 알고 있어야합니다.


isInstallPackage_2(this,"com.test.pkgname");

/**
* 설치여부를 판단한다
* @param context context
* @param pkgName 패키지명
* @return 설치여부
*/
public static String isInstallPackage_2(Context context, String pkgName){
PackageManager manager = context.getPackageManager();
PackageInfo pi;
try {
pi = manager.getPackageInfo(pkgName, PackageManager.GET_META_DATA);
if(pi!=null){
return pi.versionName+"";
}
} catch (NameNotFoundException e) {
}
return null;
}


반응형
반응형

매니페스트에 다음과 같이 밑줄 된 값이 필요할때 다음과 같이 가져온다.


AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.si.pe.shin.library"
android:versionCode="1"
android:versionName="3.7.0" >

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:description="@string/app_name"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".activity.MainActivity"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

/**
* 매니페스트 버전을 얻는다
* @param context context
* @return 버전
*/
public static String getVersionName(Context context) {
String version = "";
try {
PackageInfo i = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
version = i.versionName + "";
} catch(NameNotFoundException e) {
}
return version;
}
/**
* 매니페스트 버전코드를 얻는다
* @param context context
* @return 버전코드
*/
public static int getVersionCode(Context context) {
int versionCode = 1;
try {
PackageInfo i = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
versionCode = i.versionCode;
} catch(NameNotFoundException e) {
}
return versionCode;
}
/**
* 매니페스트 Description를 얻는다
* @param context context
* @return 어플아이디
*/
public static String getDescription(Context context) {
String appId = "";
try {
PackageManager pm = context.getPackageManager();
PackageInfo i = pm.getPackageInfo(context.getPackageName(), 0);
appId = i.applicationInfo.loadDescription(pm) + "";
} catch(NameNotFoundException e) {
}
return appId;
}

/**
* 매니페스트 Label을 얻는다
* @param context context
*/
public static String getAppLabel(Context context) {
String appName = "";
try {
PackageManager pm = context.getPackageManager();
PackageInfo i = pm.getPackageInfo(context.getPackageName(), 0);
appName = i.applicationInfo.loadLabel(pm) + "";
} catch(NameNotFoundException e) {
}
return appName;
}


반응형
반응형

개발을 하다보면 특정앱은 백그라운드에 남기지 말아야할때가 있습니다.


그럴때는 


        <activity

            android:name=".MainActivity"

            android:label="@string/app_name" 

            android:excludeFromRecents="true"

            >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>


밑에 코드를 매니페스트 액티비티에 사용해주시면 앱이 백그라운드에 남아 있지않게 됩니다.

android:excludeFromRecents="true"


밑에 그림을 보시면 현재 저는 Test을 실행시키고 스마트폰 백그라운드를 열었습니다.

이렇게 그림만 보면 모르겠지만 원래는 Test앱이 네이버앱 앞에 표시되어야합니다.

하지만 저는 android:excludeFromRecents="true"해당 코드를 사용하여

앱이 백그라운드에 표시되지 않도록 하였습니다.

반응형
반응형

첫문자를 숫자를 입력할 경우숫자만 입력되고

첫문자를 영어를 입력할 경우 영어만 입력되는 예제


private EditText mEdit;


@Override

protected void onCreateX(Bundle savedInstanceState) {

mEdit = (EditText)findViewById(R.id.test_edit);

mEdit.addTextChangedListener(new TextWatcher() {

@Override

public void onTextChanged(CharSequence s, int start, int before, int count) {

// TODO Auto-generated method stub

if(count == 0){

mEdit.setFilters(new InputFilter[]{filterAll});

}else if(count == 1){

if(isNumber(s.toString())){

mEdit.setFilters(new InputFilter[]{filterNum});

}else{

mEdit.setFilters(new InputFilter[]{filterEng});

}

}

}

@Override

public void beforeTextChanged(CharSequence s, int start, int count,

int after) {

// TODO Auto-generated method stub

}

@Override

public void afterTextChanged(Editable s) {

// TODO Auto-generated method stub

}

});

}

   public static boolean isNumber(String str){

       boolean result = false;

       try{

           Double.parseDouble(str) ;

           result = true ;

       }catch(Exception e){}


       return result ;

   }

    

    private InputFilter filterNum = new InputFilter() {

@Override

public CharSequence filter(CharSequence source, int start, int end,

Spanned dest, int dstart, int dend) {

// TODO Auto-generated method stub

Pattern ps = Pattern.compile("^[0-9]+$");

if(!ps.matcher(source).matches()){

return "";

}

return null;

}

};

    private InputFilter filterEng = new InputFilter() {

@Override

public CharSequence filter(CharSequence source, int start, int end,

Spanned dest, int dstart, int dend) {

// TODO Auto-generated method stub

Pattern ps = Pattern.compile("^[a-zA-Z]+$");

if(!ps.matcher(source).matches()){

return "";

}

return null;

}

};

    private InputFilter filterAll = new InputFilter() {

@Override

public CharSequence filter(CharSequence source, int start, int end,

Spanned dest, int dstart, int dend) {

return source;

}

};




반응형
반응형

안드로이드 타겟버전은 정하지않고 최소버전이 12인 상태에서 Theme.Holo.Light 테마를 쓰는 상태일경우

DatePickerDialog, DatePickerDialog가 앱이 죽는 현상 발생


현재 매니페스트 상태

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testdatepicker"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="12"/>

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>


해결방법

날짜

private void showCalendarDialog() {
Context context = null;
DatePickerDialog dialog = null;
Calendar calendar = Calendar.getInstance();

// 안드로이드 7.0버전부터 Theme.Holo.Light를 지원하지 않음
if (Build.VERSION.SDK_INT >= 24) {
context = new ContextThemeWrapper(mContext, android.R.style.Theme_DeviceDefault_Light_Dialog);
}else{
context = mContext;
}

dialog = new DatePickerDialog(context, mDateSetListener, calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));
dialog.show();
}


private DatePickerDialog.OnDateSetListener mDateSetListener = new DatePickerDialog.OnDateSetListener() {

@Override
public void onDateSet(DatePicker view, int year, int monthOfYear,
int dayOfMonth) {
// TODO Auto-generated method stub

}

};


시간

private void showTimeDialog() {

Context context = null;
TimePickerDialog dialog = null;
Calendar calendar = Calendar.getInstance();

// 안드로이드 7.0버전부터 Theme.Holo.Light를 지원하지 않음
if (Build.VERSION.SDK_INT >= 24) {
context = new ContextThemeWrapper(mContext, android.R.style.Theme_DeviceDefault_Light_Dialog);
}else{
context = mContext;
}

dialog = new TimePickerDialog(context, mTimeSetListener, calendar.get(Calendar.HOUR_OF_DAY), 0, true);
dialog.show();
}

private TimePickerDialog.OnTimeSetListener mTimeSetListener = new TimePickerDialog.OnTimeSetListener() {

@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
// TODO Auto-generated method stub

}

};



어떤 테마스타일 이쁜지 확인하고 싶을경우

MainActivity.class


import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TimePicker;
import android.widget.Toast;

public class MainActivity extends Activity implements OnItemClickListener {
private final static boolean isShowDatePickerDialog = true; // DatePickerDialog 노출여부

private Context mContext = null;
private LinearLayout mLinearLayout = null;
private ListView mListView = null;
private int[] mStyleIdArr = {
android.R.style.Theme,
android.R.style.Theme_Black,
android.R.style.Theme_Black_NoTitleBar,
android.R.style.Theme_Black_NoTitleBar_Fullscreen,
android.R.style.Theme_DeviceDefault,
android.R.style.Theme_DeviceDefault_Dialog,
android.R.style.Theme_DeviceDefault_Dialog_MinWidth,
android.R.style.Theme_DeviceDefault_Dialog_NoActionBar,
android.R.style.Theme_DeviceDefault_Dialog_NoActionBar_MinWidth,
android.R.style.Theme_DeviceDefault_DialogWhenLarge,
android.R.style.Theme_DeviceDefault_DialogWhenLarge_NoActionBar,
android.R.style.Theme_DeviceDefault_InputMethod,
android.R.style.Theme_DeviceDefault_Light,
android.R.style.Theme_DeviceDefault_Light_DarkActionBar,
android.R.style.Theme_DeviceDefault_Light_Dialog,
android.R.style.Theme_DeviceDefault_Light_Dialog_MinWidth,
android.R.style.Theme_DeviceDefault_Light_Dialog_NoActionBar,
android.R.style.Theme_DeviceDefault_Light_Dialog_NoActionBar_MinWidth,
android.R.style.Theme_DeviceDefault_Light_DialogWhenLarge,
android.R.style.Theme_DeviceDefault_Light_DialogWhenLarge_NoActionBar,
android.R.style.Theme_DeviceDefault_Light_NoActionBar,
android.R.style.Theme_DeviceDefault_Light_NoActionBar_Fullscreen,
android.R.style.Theme_DeviceDefault_Light_Panel,
android.R.style.Theme_DeviceDefault_NoActionBar,
android.R.style.Theme_DeviceDefault_NoActionBar_Fullscreen,
android.R.style.Theme_DeviceDefault_Panel,
android.R.style.Theme_DeviceDefault_Wallpaper,
android.R.style.Theme_DeviceDefault_Wallpaper_NoTitleBar,
android.R.style.Theme_Dialog, android.R.style.Theme_Holo,
android.R.style.Theme_Holo_Dialog,
android.R.style.Theme_Holo_Dialog_MinWidth,
android.R.style.Theme_Holo_Dialog_NoActionBar,
android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
android.R.style.Theme_Holo_DialogWhenLarge,
android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
android.R.style.Theme_Holo_InputMethod,
android.R.style.Theme_Holo_Light,
android.R.style.Theme_Holo_Light_DarkActionBar,
android.R.style.Theme_Holo_Light_Dialog,
android.R.style.Theme_Holo_Light_Dialog_MinWidth,
android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
android.R.style.Theme_Holo_Light_DialogWhenLarge,
android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
android.R.style.Theme_Holo_Light_NoActionBar,
android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
android.R.style.Theme_Holo_Light_Panel,
android.R.style.Theme_Holo_NoActionBar,
android.R.style.Theme_Holo_NoActionBar_Fullscreen,
android.R.style.Theme_Holo_Panel,
android.R.style.Theme_Holo_Wallpaper,
android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
android.R.style.Theme_InputMethod, android.R.style.Theme_Light,
android.R.style.Theme_Light_NoTitleBar,
android.R.style.Theme_Light_NoTitleBar_Fullscreen,
android.R.style.Theme_Light_Panel,
android.R.style.Theme_Light_WallpaperSettings,
android.R.style.Theme_NoDisplay, android.R.style.Theme_NoTitleBar,
android.R.style.Theme_NoTitleBar_Fullscreen,
android.R.style.Theme_NoTitleBar_OverlayActionModes,
android.R.style.Theme_Panel, android.R.style.Theme_Translucent,
android.R.style.Theme_Translucent_NoTitleBar,
android.R.style.Theme_Translucent_NoTitleBar_Fullscreen,
android.R.style.Theme_Wallpaper,
android.R.style.Theme_Wallpaper_NoTitleBar,
android.R.style.Theme_Wallpaper_NoTitleBar_Fullscreen,
android.R.style.Theme_WallpaperSettings,
android.R.style.Theme_WithActionBar };

private String[] mStyleNameArr = { "Theme", "Theme_Black",
"Theme_Black_NoTitleBar", "Theme_Black_NoTitleBar_Fullscreen",
"Theme_DeviceDefault", "Theme_DeviceDefault_Dialog",
"Theme_DeviceDefault_Dialog_MinWidth",
"Theme_DeviceDefault_Dialog_NoActionBar",
"Theme_DeviceDefault_Dialog_NoActionBar_MinWidth",
"Theme_DeviceDefault_DialogWhenLarge",
"Theme_DeviceDefault_DialogWhenLarge_NoActionBar",
"Theme_DeviceDefault_InputMethod", "Theme_DeviceDefault_Light",
"Theme_DeviceDefault_Light_DarkActionBar",
"Theme_DeviceDefault_Light_Dialog",
"Theme_DeviceDefault_Light_Dialog_MinWidth",
"Theme_DeviceDefault_Light_Dialog_NoActionBar",
"Theme_DeviceDefault_Light_Dialog_NoActionBar_MinWidth",
"Theme_DeviceDefault_Light_DialogWhenLarge",
"Theme_DeviceDefault_Light_DialogWhenLarge_NoActionBar",
"Theme_DeviceDefault_Light_NoActionBar",
"Theme_DeviceDefault_Light_NoActionBar_Fullscreen",
"Theme_DeviceDefault_Light_Panel",
"Theme_DeviceDefault_NoActionBar",
"Theme_DeviceDefault_NoActionBar_Fullscreen",
"Theme_DeviceDefault_Panel", "Theme_DeviceDefault_Wallpaper",
"Theme_DeviceDefault_Wallpaper_NoTitleBar", "Theme_Dialog",
"Theme_Holo", "Theme_Holo_Dialog", "Theme_Holo_Dialog_MinWidth",
"Theme_Holo_Dialog_NoActionBar",
"Theme_Holo_Dialog_NoActionBar_MinWidth",
"Theme_Holo_DialogWhenLarge",
"Theme_Holo_DialogWhenLarge_NoActionBar", "Theme_Holo_InputMethod",
"Theme_Holo_Light", "Theme_Holo_Light_DarkActionBar",
"Theme_Holo_Light_Dialog", "Theme_Holo_Light_Dialog_MinWidth",
"Theme_Holo_Light_Dialog_NoActionBar",
"Theme_Holo_Light_Dialog_NoActionBar_MinWidth",
"Theme_Holo_Light_DialogWhenLarge",
"Theme_Holo_Light_DialogWhenLarge_NoActionBar",
"Theme_Holo_Light_NoActionBar",
"Theme_Holo_Light_NoActionBar_Fullscreen",
"Theme_Holo_Light_Panel", "Theme_Holo_NoActionBar",
"Theme_Holo_NoActionBar_Fullscreen", "Theme_Holo_Panel",
"Theme_Holo_Wallpaper", "Theme_Holo_Wallpaper_NoTitleBar",
"Theme_InputMethod", "Theme_Light", "Theme_Light_NoTitleBar",
"Theme_Light_NoTitleBar_Fullscreen", "Theme_Light_Panel",
"Theme_Light_WallpaperSettings", "Theme_NoDisplay",
"Theme_NoTitleBar", "Theme_NoTitleBar_Fullscreen",
"Theme_NoTitleBar_OverlayActionModes", "Theme_Panel",
"Theme_Translucent", "Theme_Translucent_NoTitleBar",
"Theme_Translucent_NoTitleBar_Fullscreen", "Theme_Wallpaper",
"Theme_Wallpaper_NoTitleBar",
"Theme_Wallpaper_NoTitleBar_Fullscreen", "Theme_WallpaperSettings",
"Theme_WithActionBar" };

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
mListView = (ListView) findViewById(R.id.test_list);
for (int i = 0; i < mStyleIdArr.length; i++) {
Button btnButton = new Button(this);
btnButton.setText(mStyleNameArr[i]);
}
ArrayAdapter<String> testAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, mStyleNameArr);
mListView.setAdapter(testAdapter);
mListView.setOnItemClickListener(this);

}

//날짜 다이얼로그
private void showCalendarDialog(int style) {
Calendar calendar = Calendar.getInstance();

DatePickerDialog dialog = null;

dialog = new DatePickerDialog(mContext, style, mDateSetListener,
calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH));

dialog.show();

}

//시간 다이얼로그
private void showTimeDialog(int style) {
Calendar calendar = Calendar.getInstance();
TimePickerDialog dialog = null;
dialog = new TimePickerDialog(mContext, style, mTimeSetListener,
calendar.get(Calendar.HOUR_OF_DAY), 0, true);

dialog.show();

}

private DatePickerDialog.OnDateSetListener mDateSetListener = new DatePickerDialog.OnDateSetListener() {

@Override
public void onDateSet(DatePicker view, int year, int monthOfYear,
int dayOfMonth) {
// TODO Auto-generated method stub

}

};

private TimePickerDialog.OnTimeSetListener mTimeSetListener = new TimePickerDialog.OnTimeSetListener() {

@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
// TODO Auto-generated method stub

}

};

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// TODO Auto-generated method stub
Toast.makeText(this, "position" + position, Toast.LENGTH_SHORT).show();
if(isShowDatePickerDialog) {
showCalendarDialog(mStyleIdArr[position]);
}else{
showTimeDialog(mStyleIdArr[position]);
}
}


}


activity_main.xml

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


<ListView
android:id="@+id/test_list"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>

</RelativeLayout>


반응형
반응형

/**파일 확장자 가져오기
* @param fileStr 경로나 파일이름
* @return*/
public static String getExtension(String fileStr){
String fileExtension = fileStr.substring(fileStr.lastIndexOf(".")+1,fileStr.length());
return TextUtils.isEmpty(fileExtension) ? null : fileExtension;
}

/**파일 이름 가져오기
* @param fileStr 파일 경로
* @param isExtension 확장자 포함 여부
* @return */
public static String getFileName(String fileStr , boolean isExtension){
String fileName = null;
if(isExtension)
{
fileName = fileStr.substring(fileStr.lastIndexOf("/"),fileStr.lastIndexOf("."));
}else{
fileName = fileStr.substring(fileStr.lastIndexOf("/")+1);
}
return fileName;
}


반응형
반응형


FileCache.java

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

public interface FileCache {

public FileEntry get(String key);

public void put(String key, ByteProvider provider) throws IOException;

public void put(String key, InputStream is) throws IOException;

public void put(String key, File sourceFile, boolean move) throws IOException;

public void remove(String key);

public void clear();

public boolean has(String key);
}


FileEntry.java

public class FileEntry {

private String key;
private File file;

public FileEntry(String key, File file) {
this.key = key;
this.file = file;
}

public InputStream getInputStream() throws IOException {
return new BufferedInputStream(new FileInputStream(file));
}

public String getKey() {
return key;
}

public File getFile() {
return file;
}

}


FileCacheImpl.java

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

public class FileCacheImpl implements FileCache {

private CacheStorage cacheStorage;

public FileCacheImpl(File cacheDir, int maxKBSizes) {
long maxBytesSize = maxKBSizes <= 0 ? 0 : maxKBSizes * 1024;
cacheStorage = new CacheStorage(cacheDir, maxBytesSize);
}

@Override
public FileEntry get(String key) {
File file = cacheStorage.get(keyToFilename(key));
if (file == null) {
return null;
}
if (file.exists()) {
return new FileEntry(key, file);
}
return null;
}

@Override
public void put(String key, ByteProvider provider) throws IOException {
cacheStorage.write(keyToFilename(key), provider);
}

@Override
public void put(String key, InputStream is) throws IOException {
put(key, ByteProviderUtil.create(is));
}

@Override
public void put(String key, File sourceFile, boolean move)
throws IOException {
if (move) {
cacheStorage.move(keyToFilename(key), sourceFile);
} else {
put(key, ByteProviderUtil.create(sourceFile));
}
}

@Override
public void remove(String key) {
cacheStorage.delete(keyToFilename(key));
}

private String keyToFilename(String key) {
String filename = key.replace(":", "_");
filename = filename.replace("/", "_s_");
filename = filename.replace("\\", "_bs_");
filename = filename.replace("&", "_bs_");
filename = filename.replace("*", "_start_");
filename = filename.replace("?", "_q_");
filename = filename.replace("|", "_or_");
filename = filename.replace(">", "_gt_");
filename = filename.replace("<", "_lt_");
return filename;
}

@Override
public void clear() {
cacheStorage.deleteAll();
}

@Override
public boolean has(String key) {
return cacheStorage.has(key);
}


}


IOUtils.java

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

public abstract class IOUtils {

public static String read(InputStream is) throws IOException {
InputStreamReader reader = null;
try {
reader = new InputStreamReader(is);
StringBuilder builder = new StringBuilder();
char[] readDate = new char[1024];
int len = -1;
while ((len = reader.read(readDate)) != -1) {
builder.append(readDate, 0, len);
}
return builder.toString();
} finally {
close(reader);
}
}

public static void copy(InputStream is, OutputStream out)
throws IOException {
byte[] buff = new byte[4096];
int len = -1;
while ((len = is.read(buff)) != -1) {
out.write(buff, 0, len);
}
}


public static void copy(File source, OutputStream os) throws IOException {
BufferedInputStream is = null;
try {
is = new BufferedInputStream(new FileInputStream(source));
IOUtils.copy(is, os);
} finally {
IOUtils.close(is);
}
}

public static void copy(InputStream is, File target) throws IOException {
OutputStream os = null;
try {
os = new BufferedOutputStream(new FileOutputStream(target));
IOUtils.copy(is, os);
} finally {
IOUtils.close(os);
}
}

public static void copy(String str, OutputStream os) throws IOException {
os.write(str.getBytes());
}

public static void close(Closeable stream) {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}

}


FileCacheFactory.java

import android.content.Context;
import java.io.File;
import java.util.HashMap;

public class FileCacheFactory {

private static boolean initialized = false;
private static FileCacheFactory instance = new FileCacheFactory();

public static void initialize(Context context, String file_dir) {
if (!initialized) {
synchronized (instance) {
if (!initialized) {
instance.init(context,file_dir);
initialized = true;
}
}
}
}

public static FileCacheFactory getInstance() {
if (!initialized) {
throw new IllegalStateException(
"Not initialized. You must call FileCacheFactory.initialize() before getInstance()");
}
return instance;
}

private HashMap<String, FileCache> mCacheMap = new HashMap<String, FileCache>();
private File mCacheBaseDir;

private FileCacheFactory() {
}

private void init(Context context) {
mCacheBaseDir = context.getCacheDir();
}

private void init(Context context, String file_dir) {
// cacheBaseDir = context.getCacheDir();
mCacheBaseDir = new File(file_dir);
}

public FileCache create(String cacheName, int maxKbSizes) {
synchronized (mCacheMap) {
FileCache cache = mCacheMap.get(cacheName);
File cacheDir = new File(mCacheBaseDir, cacheName);
if (cache != null) {
try {
cache = new FileCacheImpl(cacheDir, maxKbSizes);
mCacheMap.put(cacheName, cache);
} catch (Exception e) {
String.format("FileCache[%s] Aleady exists", cacheName);
}
}


return cache;
}
}

public FileCache get(String cacheName) {
synchronized (mCacheMap) {
FileCache cache = mCacheMap.get(cacheName);
if (cache == null) {
try {

}catch (Exception e)
{
String.format("FileCache[%s] not founds.", cacheName);
}
}
return cache;
}
}

public void destroy(String cacheName)
{
FileCache cache = mCacheMap.get(cacheName);

File file = new File(mCacheBaseDir+File.separator+cacheName);
if(file.exists())
{
file.delete();
}
}

public void clear(){
mCacheMap.clear();
}

public boolean has(String cacheName) {
return mCacheMap.containsKey(cacheName);
}
}

설명

캐시 디렉토리 안에 캐시파일이 여러개 저장되는 방식입니다. 


사용방법

1. 캐시 디렉토리

private FileCache mFileCache = null;

public static final String CACHE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "FolderName" + File.separator + ".cache"; //캐시 기본폴더

FileCacheFactory.initialize(mContext, CACHE_PATH);
if (!FileCacheFactory.getInstance().has(zipDirName)) // 해당 키의 캐시 디렉토리가 있는지 확인
{
FileCacheFactory.getInstance().create(zipDirName, 0); // 캐시디렉토리가 없을경우 만든다.
}
mFileCache = FileCacheFactory.getInstance().get(zipDirName); // 해당 파일의 캐시 디렉토리를 가져온다.


- 캐시 객체 생성

FileCacheFactory.initialize(mContext, CACHE_PATH);

- 캐시디렉토리 존재 여부 체크

FileCacheFactory.getInstance().has(Dirkey)

- 캐시디렉토리 생성

FileCacheFactory.getInstance().create(Dirkey, 0); // 캐시디렉토리가 없을경우 만든다.

- 캐시디렉토리 가져오기

mFileCache = FileCacheFactory.getInstance().get(Dirkey); // 해당 파일의 캐시 디렉토리를 가져온다.


2. 캐시 

-캐시 저장

/**
* @param key cache
* @param val cache 내용
* @param isMove ture = val파일이 캐시경로로 이동됨, false = val파일이 캐시경로로 복사됨
*/
private void setCacheFile(String key, File val, boolean isMove) {
if (!mFileCache.has(key)) {
try {
mFileCache.put(key, val, isMove);
} catch (IOException e) {
e.printStackTrace();
}
}
}

-캐시 가져오기

/**
* 캐시내용을 가져온다.
* @param key cache
* @return
*/
public FileEntry getCacheFile(String key) {

return mFileCache.get(key);
}


chche.zip


반응형
반응형

Apache Commons Compress 1.14

라이브러리를 import 해줍니다.

jar파일 다운로드 url


import org.apache.commons.compress.utils.IOUtils;

/**
* 파일 복사
*
* @return exist 복사 성공 여부

*/
public synchronized boolean copyFile(String inFilePath, String outFilePath) {
FileInputStream fis = null;
FileOutputStream fos = null;
File file = null;
boolean exist = false;
try {
fis = new FileInputStream(inFilePath);
fos = new FileOutputStream(outFilePath);
IOUtils.copy(fis, fos);

file = new File(outFilePath);
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
// 마지막에 FileInputStream / FileOutputStream을 닫아준다.
if (fis != null) try {
fis.close();
}
catch (IOException e) {
Log.i("파일복사", "fileInput error");
}

if (fos != null)
try {
fos.close();
}
catch (IOException e) {
Log.i("파일복사", "fileOutput error");
}
if (file != null) {                 // 복사한 경로에 File있는지 확인
if (file.exists()) {
exist = true;
}
else {
exist = false;
}
}

}
return exist;
}



반응형
반응형

이번에 알림기능을 넣고 앱을 테스트 하면서

안드로이드 ics 버전에서 앱이 죽는 현상이 발생하여 다음과 같이 해결

public static void ViewNotice(String notiTitle ,String notiContent,Context context){ NotificationManager mNM; mNM = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);         Intent mI = new Intent(); mI.setClass(context, NotiViewActivity.class); mI.putExtra("NOTICE", notiContent); mI.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent contentIntent = PendingIntent.getActivity(context, 0, mI, PendingIntent.FLAG_UPDATE_CURRENT);     Notification notification = null;                 //안드로이드 버전 체크 if(Build.VERSION_CODES.KITKAT<Build.VERSION.SDK_INT){                     //킷켓보다 버전 보다 높을 경우

Notification.Builder builder = new Notification.Builder(context); builder.setSmallIcon(R.drawable.icon_t); builder.setWhen(System.currentTimeMillis()); builder.setContentTitle("공지 제목"); builder.setContentText("공지 내용"); builder.setContentIntent(contentIntent); notification = builder.build(); }else{                     //킷켓 버전이하일 경우

    notification = new Notification(R.drawable.icon_t, null, System.currentTimeMillis());     notification.setLatestEventInfo(context, "공지 제목","공지 내용", contentIntent); } notification.flags |= Notification.FLAG_AUTO_CANCEL; notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE; notification.defaults |= Notification.DEFAULT_SOUND; mNM.notify(0, notification); }


특정버전 이하에서는 Notification notification = builder.build(); 지원하지 않는것 같음

안드로이드 ics 버전에서는 다음과 같이 정의하면 오류가 나지 않는다.

		    notification = new Notification(R.drawable.icon_t, null, System.currentTimeMillis());
	            notification.setLatestEventInfo(context, "공지 제목","공지 내용", contentIntent);




반응형
반응형

https://developer.android.com/preview/features/working-with-fonts.html?hl=ko#fonts-in-xml 

해당 페이지에서 번역하여 저 나름대로의 주관적인 의견을 더한 게시물입니다.


Android O에는 XML안에서 font리소스를 사용할 수 있는 새로운 기능이 추가되었습니다.

이제 색다른 글씨체로 안드로이드 앱을 구현할 수 있습니다.(일이 늘어날거 같은 느낌...)

Android O는 시스템 font와 관련된 정보를 검색하고 파일 설명자를 제공하는 메커니즘도 제공합니다.


Font 및 XML

Android O를 사용하면 font파일을 res / font / (요기)  해당 경로에 font를 추가하여 글꼴들을 한데 모아놓을 수 있습니다. 

이 글꼴은 R 파일에서 컴파일되며 Android Studio에서 자동으로 사용할 수 있습니다.

기존에 리소스 R.id 처럼 글꼴을 인식할 수 있습니다.

@font/myfont, or R.font.myfont.

font를 리소스로 추가하려면 Android Studio에서 다음 단계를 수행하십시오.

1. res 폴더를 마우스 오른쪽 버튼으로 클릭하고 새로 만들기> Android 리소스 디렉토리로 이동하십시오. New Resource Directory 창이 나타납니다.

2. 리소스 종류 목록에서 font을 선택한 다음 확인을 누릅니다.

참고: font파일을 넣을 디렉토리의 이름은 무조건 font여야합니다.


font 디렉토리를 만든다.


3. font 디렉토리에 font파일 추가 합니다.

아래 그림처럼 font 디렉토리에 font파일을 넣어 두면 R.font.dancing_scriptR.font.lobster 해당이름으로 font를 불러올 수 있습니다.



4. font파일을 두번 클릭하면 편집기에서 해당 파일의 font를 미리볼 수 있습니다.


XML 레이아웃에서 font를 사용

TextView 객체나 style에서 font를 쉽게 사용할 수 있습니다.

TextView 객체나 style에서 font를 연결하려면 fontFamily 특성을 사용하면 됩니다.

- TextView에 font 추가

<TextView
       
android:layout_width="wrap_content"
       
android:layout_height="wrap_content"
       
android:fontFamily="@font/lobster"/>

- Style에 font 추가

<style name="customfontstyle" parent="@android:style/TextAppearance.Small">
   
<item name="android:fontFamily">@font/lobster</item>
</style>

- 코드에서 font 추가

Typeface typeface = getResources().getFont(R.font.myfont);
textView
.setTypeface(typeface);


더욱 자세한 사항은 해당페이지에서 확인 하세요.

https://developer.android.com/preview/features/working-with-fonts.html?hl=ko#retrieving-system-fonts

반응형

+ Recent posts