Volley를 이용하여 간단한 HTTP 통신을 하는 예제이다.


1. AndroidManifest.xml 에서 권한 명시 

<uses-permission android:name="android.permission.INTERNET" />


2. build.gradle 내  dependencies 에 Volley 사용을 위한 명시

compile 'com.android.volley:volley:1.1.0'


3. Build - Make Project 를 하여 프로젝트에서 Volley 사용 가능


4. HTTP 통신 예제

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log.i
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

lateinit var request: RequestQueue
lateinit var stringRequest: StringRequest

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

// Setup RequestQueue
request = Volley.newRequestQueue(this)
val url = "http://www.google.com"

// Setup StringRequest
stringRequest = StringRequest(Request.Method.GET, url, Response.Listener<String> {
response -> textView1.text = response // Print http source using textview
}, Response.ErrorListener {
error -> i(applicationContext.packageName, error.toString()) // Print log if error occurred
})

// Set tag for cancel
stringRequest.tag = applicationContext.packageName
// Request
request.add(stringRequest)
}

override fun onStop() {
super.onStop()

// Cancel all request that have packageName tag
if(request != null) {
request.cancelAll(applicationContext.packageName)
}
}
}


GitHub : https://github.com/Hot6ix/SimpleHttpRequest


출처 : https://developer.android.com/training/volley/simple.html

by JamesY 2017. 12. 21. 16:30

안드로이드 6.0 이후부터는 일부 권한의 경우 단순히 AndroidManifest.xml 에 명시하더라도 작동이 되지 않는다.


설치될 때 권한을 부여하는 것이 아니라 실행되는 도중 런타임에 권한을 요청하기 때문이다.


하지만 여전히 AndroidManifest.xml 에 권한을 명시해야 한다.


예제의 경우 주소록 읽기 권한을 요청한다.


권한 요청


if (ContextCompat.checkSelfPermission(thisActivity,
               
Manifest.permission.READ_CONTACTS)
       
!= PackageManager.PERMISSION_GRANTED) {

       
// 권한이 없으므로 요청
       
ActivityCompat.requestPermissions(thisActivity,
               
new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS
);

       
// MY_PERMISSIONS_REQUEST_READ_CONTACTS 의 경우 사용자 정의 값으로
       
// 응답 처리에 필요
}


응답 처리


@Override
public void onRequestPermissionsResult(int requestCode,
       
String permissions[], int[] grantResults) {
   
switch (requestCode) {
       
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
           
// If request is cancelled, the result arrays are empty.
           
if (grantResults.length > 0
               
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {

               
// 권한 획득

           
} else {

               
// 권한 획득 실패
           
}
           
return;
       
}

       
// other 'case' lines to check for other
       
// permissions this app might request
   
}
}


별도의 요청이 필요한 권한 목록


CALENDAR
CAMERA
CONTACTS
LOCATION
MICROPHONE
PHONE
SENSORS
SMS
STORAGE


출처 : https://developer.android.com/training/permissions/requesting.html?hl=ko

by JamesY 2017. 12. 21. 14:06


사진 1


Navigation Drawer(네비게이션 드로워)

안드로이드가 발전하고 있는 만큼 화면의 효율적인 활용이 중요시 되고 있다.  ActionBar, Fragment 등을 이용하여 화면을 분할하고 각각에 역할을 줄 수 있게 되었다. Navigation Drawer도 화면을 효율적으로 사용할 수 있게 해주는 방법 중 하나이다. 대부분의 구글 앱에는 Navigation Drawer가 적용되어 있고, 인기 앱에도 자주 사용되고 있다.

소스 다운로드 및 소개 페이지

http://developer.android.com/intl/ko/training/implementing-navigation/nav-drawer.html  에서

오른쪽 중간 부분에 'Download the sample app'을 다운하면 된다.

또는

NavigationDrawer.zip


소스를 보면 메인 클래스 하나가 전부이다.

변수 정의

    private DrawerLayout mDrawerLayout; // 주 기능
    private ListView mDrawerList; // 내용
    private ActionBarDrawerToggle mDrawerToggle; // 주 기능

    private CharSequence mDrawerTitle; // ActionBar의 제목을 변경하기 위한 변수
    private CharSequence mTitle; // ActionBar의 제목을 변경하기 위한 변수
    private String[] mPlanetTitles; // 태양계 행성 이름들

onCreate()

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 액션바 제목 mTitle = mDrawerTitle = getTitle(); // strings.xml의 데이터 삽입 mPlanetTitles = getResources().getStringArray(R.array.planets_array); // DrawerLayout 정의 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); // DrawerLayout Shadow 정의(사진 2 참조) mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); // ListView 정의 mDrawerList = (ListView) findViewById(R.id.left_drawer); // ListView 데이터 정의 mDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, mPlanetTitles)); // ListView 아이템 클릭 리스너 mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); // ActionBar의 홈버튼을 Navigation Drawer 토글기능으로 사용 getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeButtonEnabled(true); // 토글 정의 mDrawerToggle = new ActionBarDrawerToggle( this, mDrawerLayout, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close ) { public void onDrawerClosed(View view) { getActionBar().setTitle(mTitle); invalidateOptionsMenu(); } public void onDrawerOpened(View drawerView) { getActionBar().setTitle(mDrawerTitle); invalidateOptionsMenu(); } }; // Drawer Layout의 리스너를 mDrawerToggle로 정의 mDrawerLayout.setDrawerListener(mDrawerToggle); // 인스턴스 상태가 존재 안하면 가장 첫번째 아이템으로 시작 if (savedInstanceState == null) { selectItem(0); } }


사진 2

DrawerItemClickListener() & selectItem()

private class DrawerItemClickListener implements ListView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            selectItem(position);
        }
    }

    private void selectItem(int position) {
        ⁄// 리스트 아이템 클릭 시 변경됨
        Fragment fragment = new PlanetFragment();

        // Fragment에 추가적인 정보 저장
        Bundle args = new Bundle();
        args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
        fragment.setArguments(args);

        // FragmentManger가 Fragment가 바뀔때마다 교체해줌
        FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();

        // 아이템이 계속 클릭된 상태로 유지
        mDrawerList.setItemChecked(position, true);

        // ActionBar 제목 변경(해당 글에는 넣지 않음.)
        setTitle(mPlanetTitles[position]);

        // Drawer Layout 닫기
        mDrawerLayout.closeDrawer(mDrawerList);
    }

PlanetFragment Class

public static class PlanetFragment extends Fragment { public static final String ARG_PLANET_NUMBER = "planet_number"; public PlanetFragment() { // Fragment 하위 클래스 때문에 필요함 } // 기본 Fragment의 화면 정의 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_planet, container, false); int i = getArguments().getInt(ARG_PLANET_NUMBER); String planet = getResources().getStringArray(R.array.planets_array)[i]; int imageId = getResources().getIdentifier(planet.toLowerCase(Locale.getDefault()), "drawable", getActivity().getPackageName()); ((ImageView) rootView.findViewById(R.id.image)).setImageResource(imageId); getActivity().setTitle(planet); return rootView; } }

이 외에도 여러 메소드들이 존재하지만 이 글에는 최소한의 Drawer Layout 생성 및 작동에 필요한 메소드들과 클래스만 적어두었다. 

activity_main.xml

<android.support.v4.widget.DrawerLayout
    xmlns:android="http:⁄⁄schemas.android.com⁄apk⁄res⁄android"
    android:id="@+id⁄drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <FrameLayout
        android:id="@+id⁄content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" ⁄>
    
    <ListView
        android:id="@+id⁄left_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color⁄transparent"
        android:dividerHeight="0dp"
        android:background="@android:color⁄white"⁄>
<⁄android.support.v4.widget.DrawerLayout>

DrawerLayout에 FrameLayout과 ListView를 넣어 구현하였다.

FrameLayout말고 다른 Layout이 삽입되어도 된다.

drawer_list_item.xml

<TextView xmlns:android="http:⁄⁄schemas.android.com⁄apk⁄res⁄android"
    android:id="@android:id⁄text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr⁄textAppearanceListItemSmall"
    android:gravity="center_vertical"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:textColor="@android:color⁄black"
    android:background="?android:attr⁄activatedBackgroundIndicator"
    android:minHeight="?android:attr⁄listPreferredItemHeightSmall"⁄>

각각의 리스트 아이템에 적용되는 속성이다.

fragment_planet.xml

<ImageView xmlns:android="http:⁄⁄schemas.android.com⁄apk⁄res⁄android"
    android:id="@+id⁄image"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000"
    android:gravity="center"
    android:padding="32dp" ⁄>

각각의 Fragment의 이미지들의 대한 속성이다.



구글이 제공하는 소스에는 ListView를 이용한 Drawer Layout을 구현되어 있다. 하지만 이 ListView를 좀 더 커스텀하여 꾸밀 수도 있고, ListView가 아닌 Layout을 넣음으로써 텍스트뷰, 버튼, Radio 버튼, 체크박스, 레이아웃 종류 등 다양한 시도를 할 수 있다.

크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 4.0 국제 라이선스에 따라 이용할 수 있습니다.
by JamesY 2013. 11. 13. 19:46

출처 : http://android-codes-examples.blogspot.kr/2011/03/customized-listview-items-selection.html

나도 다른 곳에서 한참 헤매다 어느 사이트에서 여기를 알려주었다.

중요한 것은

색을 지정한 list_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="false"
android:state_pressed="false"
android:drawable="@color/grey" />
<item android:state_pressed="true"
android:drawable="@color/blue" />
<item android:state_selected="true"
android:state_pressed="false"
android:drawable="@color/blue" />
</selector>

을 정해주고

이 xml을 커스텀한 리스트 xml에 있는 주요 layout의 background로 지정해준다.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_margin="2dp" android:background="@color/list_bg">

<TextView android:id="@+id/post" android:gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="50dp"
android:textSize="20sp" android:textColor="#D0640D"
android:layout_toRightOf="@+id/bite_image" />

</RelativeLayout>

중요한 부분을 굵게 표시하였다.


'Android > Tips' 카테고리의 다른 글

런타임에 권한 요청  (0) 2017.12.21
Navigation Drawer 분석  (1) 2013.11.13
커스텀 리스트뷰 만들기  (0) 2012.10.14
안드로이드 다중 replace 사용  (0) 2012.10.10
Google Places API 리뷰 가져오기 관해서...  (0) 2012.10.08
by JamesY 2012. 10. 15. 20:20

http://androiddeveloper.tistory.com/73


개인적으로 여기가 가장 커스텀리스트뷰 만들기에 정리도 잘되있고 쉽게 되있습니다.

by JamesY 2012. 10. 14. 12:33

String s = "안녕하세요.";

s = s.replace("안","앚");

s = s.replace("녕","녖");

System.out.println(s);

결과는 "안녖하세요."가 나온다.

해결법은 replace를 저렇게 따로 따로 넣지 말고 한줄에 붙인다.

s = s.replace("안","앚").replace("녕","녖").replace("하", "핮");

결과는 "앚녖핮세요."가 나오게 된다.

by JamesY 2012. 10. 10. 10:43

Google Places API가 주는 결과 중 하나인 리뷰는 사람들에게 특정한 곳에 대한 평가를 내리는 것이기 때문에, 나는 매우 중요시 여긴다.

그런데 결과를 가져올 때, 평점도 가져와야하는데 여러 기준이 있다.

overall(전체), food(맛), decor(인테리어), service(서비스), facilities(시설) 등 수많은 기준이 있다.

overall의 경우 문제가 될 것이 없다.

overall은 '전체' 기준이기 때문에 평점을 1개만 주면 되지만, 다른 기준으로 주면 3개를 주어야 한다. 예를 들어

food를 주면 decor과 service도 같이 평점을 줘야한다. 그렇게 설정되어 있다.

ex)

"aspects" : [
               {
                  "rating" : 2,
                  "type" : "overall"
               }
            ]

overall(전체)는 한개만 주기 때문에 쉽게 가져올 수 있었다.

하지만 다른 기준으로 설정하면

"aspects" : [ { "rating" : 3, "type" : "food" }, { "rating" : 3, "type" : "decor" }, { "rating" : 3, "type" : "service" } ] 이런 경우 3개를 가져와야 한다.

진짜 문제는 overall과 다른 기준이 섞여 있는 경우다.

"reviews" : [ { "aspects" : [ { "rating" : 3, "type" : "overall" } ], "author_name" : "Google 사용자", "text" : "좋아요", "time" : 1322395747 }, { "aspects" : [ { "rating" : 0, "type" : "overall" } ], "author_name" : "Google 사용자", "text" : "안좋아요.", "time" : 1306676342 }, { "aspects" : [ { "rating" : 3, "type" : "overall" } ], "author_name" : "Google 사용자", "text" : "매우 좋아요.", "time" : 1323514896 }, { "aspects" : [ { "rating" : 3, "type" : "food" }, { "rating" : 3, "type" : "decor" }, { "rating" : 3, "type" : "service" } ], "author_name" : "James Yang", "author_url" : "https://plus.google.com/104680883154257434480", "text" : "모든 것이 다 좋아요.", "time" : 1349590648 } ]

이런 경우를 ArrayList로 저장하면 이렇게 된다.

ArrayList rating = {3,0,3,3,3,3}

ArrayList author_name = {Google 사용자,Google 사용자,Google 사용자,James Yang}

Google 사용자들은 overall로 평점을 주었기 때문에, 한개만 결과를 준다.

하지만 James Yang의 경우 food, decor, service를 주었기 때문에 결과를 3개나 준다.

for(int i=0; i<author_name.size(); i++){

System.out.println(author_name.get(i)+"\n"+rating.get(i));

}

이렇게 되면 rating의 결과는 이렇게 출력된다. 3,0,3,3 // 뒤에 3,3이 빠짐

for(int i=0; i<rating.size(); i++){

System.out.println(author_name.get(i)+"\n"+rating.get(i));

}

이렇게 하면 또 오류가 발생한다. 6개를 출력해야 하는데 author_name은 4개밖에 없기 때문이다.

이제 해결책을 찾아보자.

일단 overall은 무시하자 문제는 food같은 3개를 주어야하는 평점기준들이다.

다행힌 점은 food, decor, service 3개를 주면 food가 무조건 먼저 나오기 때문에

가져올 때 type이 food인지 아닌지 확인해주면 된다.

food이면 food와 그 뒤에 있는 decor와 service를 하나의 변수에 저장하면 된다.

그러면 1개가 되기 때문에 제대로 출력이 된다.

3개 묶음

appeal, facilities, service

quality, appeal, service

food, decor, service

.....

크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 4.0 국제 라이선스에 따라 이용할 수 있습니다.
by JamesY 2012. 10. 8. 21:35

안드로이드에서 구글 맵을 써야할 일이 생겨서 인터넷에 나와있는 대로 했다.

그런데 xml쪽에서 Failed to find style 'mapViewStyle' in current theme 이라 떳다.

처음에는 이거 못고치면 실행불가라 생각했는데, 실행은 되었다.

하지만 오류를 주었는데, InflateException이 발생하였다.

무슨 에러인가 싶어서 계속 인터넷을 찾아도 답을 못찾았다.

답을 못찾은지 일주일 넘은 후, 거의 다해가는 프로젝트에 맵기능만 구현이 안되있어서

할 수 없이 다른 개발자들이 만들어 놓은 mapview 예제를 다운해서 보는데, xml에서 같은 에러가 나온다.

이클립스 버그이거나 개발환경 때문인가 싶었다. 근데 apiKey를 내껄로 바꾼 후 실행해보았더니 실행이 되었다.

그래서 바로 내 어플에 적용했더니 구현이 되었다. 일주일 동안 저 오류만 파헤쳤지만, 그냥 단순한 경고정도 였다....


MapView 구현할 시 주의사항

1. 퍼미션 설정 ( INTERNET, COARSE_LOCATION, FINE_LOCATION )

2. 해당 라이브러리를 사용하는지 확인(com.google.android.maps) * <application> 안에 넣어져 있어야함.

3. xml의 mapview 확인

4. Activity가 MapActivity인지 확인(이걸로도 조금 고생함..)

5. Build Target을 Google APIs로 설정.


맵뷰가 구현되었지만 맵타일(화면)을 받지 못하는경우

apiKey 재확인

by JamesY 2012. 10. 6. 19:45
| 1 2 |