[출처] Stack Overflow - http://stackoverflow.com/questions/4212861/what-is-a-correct-mime-type-for-docx-pptx-etc

  자세히 Intent에 대하여 정리하고 싶지만, 지금은 시간이 없으므로 일단 파일 확장자에 따라 맞는 타 앱을 Intent로 부를 때 필요한 MIME type을 기록해 두려 한다. 기본적으로 Intent intent = new Intent(Intent.ACTION_VIEW); 와 같이 선언한 뒤, intent.setDataAndType("file URI", "아래 MIME Type String"); 로 지정하여 연결한다.

Extension MIME Type
.doc     application/msword
.dot      application/msword

.docx    application/vnd.openxmlformats-officedocument.wordprocessingml.document
.dotx     application/vnd.openxmlformats-officedocument.wordprocessingml.template
.docm   application/vnd.ms-word.document.macroEnabled.12
.dotm    application/vnd.ms-word.template.macroEnabled.12

.xls      application/vnd.ms-excel
.xlt       application/vnd.ms-excel
.xla      application/vnd.ms-excel

.xlsx     application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.xltx      application/vnd.openxmlformats-officedocument.spreadsheetml.template
.xlsm    application/vnd.ms-excel.sheet.macroEnabled.12
.xltm     application/vnd.ms-excel.template.macroEnabled.12
.xlam    application/vnd.ms-excel.addin.macroEnabled.12
.xlsb     application/vnd.ms-excel.sheet.binary.macroEnabled.12

.ppt      application/vnd.ms-powerpoint
.pot      application/vnd.ms-powerpoint
.pps     application/vnd.ms-powerpoint
.ppa     application/vnd.ms-powerpoint

.pptx      application/vnd.openxmlformats-officedocument.presentationml.presentation
.potx      application/vnd.openxmlformats-officedocument.presentationml.template
.ppsx     application/vnd.openxmlformats-officedocument.presentationml.slideshow
.ppam    application/vnd.ms-powerpoint.addin.macroEnabled.12
.pptm     application/vnd.ms-powerpoint.presentation.macroEnabled.12
.potm     application/vnd.ms-powerpoint.presentation.macroEnabled.12
.ppsm    application/vnd.ms-powerpoint.slideshow.macroEnabled.12

  사실 일전의 Custom Gallery Example에서 Gallery Item Long Click시 해당 사진 파일을 삭제하는 코드를 입력했었다. 실행 결과, 파일은 삭제되기는 했지만 파일 삭제 후의 Gallery Item들의 배열이 정리되지 않았고, 원래 해당 파일이 있던 frame 내부가 null 값으로 까맣게 보이며 click 시 App.이 비정상적으로 종료되었다. 그리고 Camera Intent로 촬영 후 새로 찍은 사진이 Gallery에 넘어오지 않았다. 아무래도 file 배열이 변경된 후 Gallery Adapter를 다시 불러와야 될 것 같은데, 원래 adapter가 생성되어 있던 onCreate()를 통째로 다시 불러들여도 괜찮을지 Activity 생애주기(Life Cycle)에 대해 알아봐야 겠다.

1. 목적 : Android Activity LifeCycle(Activity 생애주기)를 이해하고 기본 구조를 알아보자.

2. 개발 환경
 - PC : Windows 7, Android Studio 1.4.1(64-bit)
 - Phone : LG G pro Lollipop(API 21)

3. 참고자료
 1) Android Developers - Activity Reference (http://developer.android.com/reference/android/app/Activity.html)

4. Activity Life Cycle(Acitivity 생애 주기)의 이론

  Android Studio로 App. project를 생성해 보면 /java/'해당 packaging 명'/MainActivity.java 이 기본으로 만들어져 있고 그 안에 파일명과 같은 class가 'extends Activity' 혹은 'extends AppCompatAcitivity'을 끌고 기본 method인 onCreate(Bundle savedInstanceState)를 @Override한 채로 만들어져 있는 광경을 볼 수 있다. 이 모습을 보다보면 아무래도 C 언어에서 void main(void) 가 생성되며 시작 지점이 자동으로 지정되듯이 onCreate()가 필수적 임을 눈치로 대강 알 수 있을텐데, 실제로 'Android Developers reference'를 찾아보면 다음과 같은 순서도를 통해서 App.의 Activity에 대한 동작 구조를 파악할 수 있다.

  보시다시피 App.이 실행되면서 Activity가 시작되면 처음에 onCreate()를 통해서 Activity가 생성되고 onStart()와 onResume()을 거처 해당 Activity가 실행된다. 그러다가 해당 Activity를 sleep 시킬 때 onPause()와 onStop()을 거치게 되고, onStop() method 시점에서 사용자가 해당 Activity를 다시 실행시키고자 하면 onRestart()를, App.이 memory를 많이 잡아먹어 시스템에 의해 process kill이 됐을 때 사용자가 원할 경우 onCreate()로 넘어간다. 이런 method들을 돌고 돌던 Activity는 끝날 때가 되면 onDestory()를 통해 system에서 사라진다.

  그래서 App.의 activity의 생애주기에 따라 작동해야 할 특정한 기능이 있다면 아래와 같이 Activity class 내에 위 method()를 따로 정의해서 사용한다. 하나의 예로서, 이전에 항상 꺼지지 않는 App.을 만들기 위해서 onStop() 을 @Override하여 onStop() 내에서 onRestart()를 무조건 실행시키게 만들었었다.

public class Activity extends ApplicationContext {
    protected void onCreate(Bundle savedInstanceState);

    protected void onStart();

    protected void onRestart();

    protected void onResume();

    protected void onPause();

    protected void onStop();

    protected void onDestroy();
}

  그럼 이번 Gallery가 새로 찍은 사진이 update되지 않는 문제는 Gallery Adapter의 생성 및 연결을 onStart() method에 선언하여 @Override 해보면 되지 않을까? 아무래도 Camera Intent를 불러오면서 기존 MainActivity는 onStop()에 있어 intent가 종료되고 나면 onStart() 부터 다시 시작할 것 같았기 때문이다.

5. 실험

  이전의 Custom Gallery Example의 Gallery Adapter 선언 부분을 단순히 onStart()로 아래와 같이 옮겨봤다.

public class MainActivity extends AppCompatActivity {

    public static final int MEDIA_TYPE_IMAGE = 1;
    public static final int MEDIA_TYPE_VIDEO = 2;
    public int inSampleSize = 1;

    private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
    private Uri fileUri;
    private static String basePath;

    public float imageViewRotation = 90;
    public String TAG = "Camera Example :: ";

    private Button takePicBtn;
    private ImageView resultView;
    private TextView imgPath;
    private Gallery customGallery;
    private CustomGalleryAdapter customGalAdapter;

    private String[] imgs;

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

        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "MyCameraApp");
        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()){
                Log.d("MyCameraApp", "failed to create directory");

            }
        }

        basePath = mediaStorageDir.getPath();

        imgPath = (TextView)findViewById(R.id.imgpath);
        resultView = (ImageView)findViewById(R.id.resultview);
        takePicBtn = (Button)findViewById(R.id.takepicbtn);
        takePicBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // create Intent to take a picture and return control to the calling application
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

                fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE); // create a file to save the image
                intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name

                // start the image capture Intent
                startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
            }
        });

       
        customGallery = (Gallery)findViewById(R.id.customgallery);


    }

    /* 아래와 같이 onStart()를 Override하여 Gallery Adapter 와 연관된 부분을 그대로 복사해 옮겨놓았다. */
    @Override
    protected void onStart(){
        super.onStart();

        File file = new File(basePath);
        imgs = file.list();
        for(int i=0; i<imgs.length; i++){
            imgPath.setText(imgs[i]);
        }

        customGalAdapter = new CustomGalleryAdapter(getApplicationContext(), basePath);
        customGallery.setAdapter(customGalAdapter);
        customGallery.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Bitmap bm = BitmapFactory.decodeFile(basePath+ File.separator +imgs[position]);
                Bitmap bm2 = ThumbnailUtils.extractThumbnail(bm, bm.getWidth() / inSampleSize, bm.getHeight() / inSampleSize);
                resultView.setImageBitmap(bm2);
                imgPath.setText(basePath+File.separator+imgs[position]);
            }
        });
        customGallery.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                File temp = new File(basePath+File.separator+imgs[position]);
                temp.delete();
                return false;
            }
        });
    }

  위 변경사항을 반영하여 compile/Run을 시켜봤더니 다행히 camera intent 실행 후 새로 찍은 사진이 gallery 에 반영되었다. 이제는 파일 삭제 후 Gallery update가 되도록 다른 method를 살펴봐야겠다.

(위 code의 실행결과)

     

 

  앞서 기본 ListView code를 보다가 Adapter를 잘 이용하면 사용자의 입맛에 맞는 ListView layout을 적용하여 Custom ListView를 생성할 수 있지 않을까 싶었다. 실제로 검색해보니 많은 분들이 CustomListView를 구성하여 활용하였고, 그 방법 또한 구체적으로 설명을 하고 있었다. 이제 나도 Customizing을 해 봐야지.



[사진 1] Android Studio에서 해당 App의 External Libraries 중 Android API로 주어진 자료들을 뒤져보면 앞서 Adapter에 적용된 'simple_list_view_1.xml'을 사진과 같이 찾을 수 있었다. 이를 통해서 Custom ListView를 만들려면 ListView에 대한 .xml 작성이 필요할 것이라는 추측이 들었다.


1. 목적 : 기본 ListView를 customizing하여 사용해 보자.

2. 개발 환경
 - PC : Windows 7, Android Studio 1.4.1(64-bit)
 - Phone : LG G pro Lollipop(API 21)

3. 참고자료
 1) Using lists in Android (ListView) - Tutorial (http://www.vogella.com/tutorials/AndroidListView/article.html)
 2) berabue 블로그 - ListView의 사용 및 Customizing (http://berabue.blogspot.kr/2014/05/android-listview.html)
 3) 미르의 IT 정복기 (http://itmir.tistory.com/477)

4. 과정
 1) 원하는 모양의 ListView Layout을 만들기 위해 listview_custom.xml을 다음과 같이 만든다. 필요에 따라 TextView를 추가하고 그림(ImageView)를 빼는 등 사용자의 입맛에 맞게 제작하면 된다. 나는 ImageView 1개와 TextView 2개를 다음과 같이 구성하였다.

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

    <ImageView
        android:id="@+id/listview_pic01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:src="@mipmap/ic_launcher"/>
    <TableLayout
        android:id="@+id/text_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/listview_pic01"
        android:layout_gravity="center_horizontal"
        android:gravity="center"
        android:padding="5dp">
        <TableRow>
            <TextView
                android:id="@+id/listview_text01"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_toRightOf="@id/listview_pic01"
                android:padding="2dp"
                android:text="Text01" />
            </TableRow>
        <TableRow>
            <TextView
                android:id="@+id/listview_text02"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:padding="2dp"
                android:text="Text02"/>
        </TableRow>
    </TableLayout>

</RelativeLayout>

 

 

 

 

 

[사진 2] Custom ListView의 Layout으로 TextView  2개와 ImageView 1개로 위 사진과 같이 구성하려 한다.

 2) Layout을 Customizing했으면 해당 Layout을 받을 ListViewAdapter도 Customizing한다. BaseAdapter를 받아 필수 요소는 아래와 같이 Override하는데, 특히 해당 위치를 보여주는 getView() 같은 경우에는 미리 Custom ListView의 요소를 Holder하여 값을 받도록 지정한다.


public class CustomListViewAdapter extends BaseAdapter {
    // CustomListView의 원소를 각각 배열(Array)로 보관할 ArrayList.
   // 현재 Code에서는 선언만 했을 뿐 사용하지 않았다. 제작자의 기호에 따라 고치면 되겠다.

    private ArrayList<ImageView> m_ListPic;
    private ArrayList<String> m_List01;
    private ArrayList<String> m_List02;

    // CustomListView의 ImageView와 TextView들을 묶어서 Array로 보관할 ArrayList와 자료를 받을 Context, adapter 선언
    private Context mContext = null;
    public ArrayList<CustomListData> mListData = new ArrayList<CustomListData>();
    private CustomListViewAdapter mAdapter = this;

    public CustomListViewAdapter(Context mContext) { // CustomListViewAdapter 생성자
        super();
        this.mContext = mContext;
    }

    @Override
    public int getCount() { // List에 속한 원소의 갯수 count method
        return mListData.size();
    }

    @Override
    public Object getItem(int position) { // CustomListView의 position 위치에 있는 Item을 반환
        return mListData.get(position);
    }

    @Override
    public long getItemId(int position) { // CustomListView의 Item의 position을 반환
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) { // CustomListView를 목록으로 보여줌
        CustomListViewHolder holder; // 이전에 정의한 CustomListView의 원소에 맞춘 Holder를 선언
        if (convertView == null) { // 아직 CustomListView의 구성이 반영되지 않은 경우, 다음과 같이 listview_custom.xml을 반영
            holder = new CustomListViewHolder();

            LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.listview_custom, null);

            holder.mListPicHolder = (ImageView) convertView.findViewById(R.id.listview_pic01);
            holder.mListText01Holder = (TextView) convertView.findViewById(R.id.listview_text01);
            holder.mListText02Holder = (TextView) convertView.findViewById(R.id.listview_text02);
            convertView.setTag(holder);
        }else{ // convertView에 구성이 이미 반영되어 있으면(null이 아니면) holder에 연결
            holder = (CustomListViewHolder) convertView.getTag();
        }

        CustomListData mData = mListData.get(position); // 위와 같이 layout 연결 후, position에 ListData를 받아와 
        if (mData.mListPic != null) { // ImageView에 입력될 data가 있을 경우 반영하고
            holder.mListPicHolder.setVisibility(View.VISIBLE);
            holder.mListPicHolder.setImageDrawable(mData.mListPic);
        }else{
            holder.mListPicHolder.setVisibility(View.GONE);
        }

        // holder에 text도 각각 반영. 필요에 따라 위와 같이 mText01/mText02 == null 여부를 따져 반영할 수 있다.
        holder.mListText01Holder.setText(mData.mText01);
        holder.mListText02Holder.setText(mData.mTest02);

        return convertView;
    }

    public void addItem(Drawable icon, String mTitle, String mDate){ // 필수 method는 아니나 ListView에 item을 반영하기 위해 필요
        CustomListData addInfo = null;
        addInfo = new CustomListData();
        addInfo.mListPic = icon;
        addInfo.mText01 = mTitle;
        addInfo.mTest02 = mDate;

        mListData.add(addInfo); // 앞에서 선언한 CustomListData를 받아 ArrayList에 통째로 add
    }

    public void remove(int position){ // 해당 position의 값을 제거하는 method
        mListData.remove(position);
        dataChange();
    }
    public void dataChange(){ // 위 remove(int position) method에서 CustomAdapter에 변경사항이 반영되도록 하는 method
        mAdapter.notifyDataSetChanged();
    }
}

public class CustomListData {
    /* CustomListView가 담을 객체에 대한 Class 생성 */
    // ImageView에 상응
    public Drawable mListPic;
    // TextView01에 상응
    public String mText01;
    // TextView02에 상응
    public String mTest02;
}
public class CustomListViewHolder {
    / * CustomListView의 구성요소에 대한 Holder 생성 */
    // Drawable을 받을 ImageView
    public ImageView mListPicHolder;
    // String mText01을 받을 TextView
    public TextView mListText01Holder;
    // String mText02를 받을 TextView
    public TextView mListText02Holder;
}

 3) 마지막으로 CustomListView를 App에 반영한다. 여기서는 MainActivity.java에 다음과 같이 사용하였다.


public class
MainActivity extends AppCompatActivity {

    public ListView listView;

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

        listView = (ListView)findViewById(R.id.listview); // 이전에 activity_main.xml에 선언되어 있던 ListView와 연결
        final CustomListViewAdapter mAdapter = new CustomListViewAdapter(this);
        listView.setAdapter(mAdapter);

        // CustomListViewAdapter의 addItem() method를 통해 Item 추가
        mAdapter.addItem(getResources().getDrawable(R.mipmap.apple), "과일", "사과");
        mAdapter.addItem(getResources().getDrawable(R.mipmap.pair), "과일", "배");
        mAdapter.addItem(getResources().getDrawable(R.mipmap.potato), "채소", "감자");
        mAdapter.addItem(getResources().getDrawable(R.mipmap.pumpkin), "채소", "호박");
        mAdapter.addItem(getResources().getDrawable(R.mipmap.onion), "채소", "양파");

        // CustomListView의 Item을 누를 경우 Toast를  출력하도록 작성
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
            @Override
            public void onItemClick(AdapterView<?> parent, final View view, int position, long id){
                CustomListData mData = mAdapter.mListData.get(position);
                Toast.makeText(getApplicationContext(), mData.mText01+" "+mData.mTest02+" is selected!", Toast.LENGTH_SHORT).show();
            }
        });
    }
}


(위 Code의 실행 결과)

     

 

+ Recent posts