블로그 이미지
엡뽀
피난(?) 오신걸 환영합니다.

calendar

1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Notice

2012. 12. 31. 17:20 Programing/Android


드디어 Android UI 만들어 보자의 마지막 포스팅이네요...;; 개인적으로 시간이 잘 안난것도 이유가 되겠지만 더 큰 이유는 귀찮니즘 때문에 너무 질질 끌어왔었네요;; (이 Androud UI 만들기 포스팅만 몇주를 하고있는 것같은;;;)


다시 한번 마음을 다잡으며 포스팅을 열심히 해볼렵니다. 아자아자!! ㅎㅎ



다시 본론으로 넘어와서, 이전 포스팅까지의 내용을 정리하여 보면, Activity를 두개 만들어서, 하나에선 기본 뷰들만 나타나고, 다른 하나에서는 리스트 뷰가 나타나도록 구성한뒤, 기본 뷰에서 Action bar를 통하여 리스트 뷰의 엑티비티가 실행되도록 프로젝트를 작성하였습니다. 


여기에 Tabbar를 추가하여 두개의 엑티비티로 되어있는 프로젝트를 하나의 엑티비티에서 두개의 뷰로 볼수있는 구조로 변경하도록 하겠습니다.  단 이 Tabbar도 오픈소스를 적용하여 진행하도록 하겠습니다. ( 처음 계획은 오픈 소스는 살며시 스쳐 지나가겠금 할려했었는데 자꾸만 오픈소스를 추가 하게 되네요;; github 도 쓸줄도 모르면서 ㅜㅜ)




▶android-viewflow 오픈소스

URL : https://github.com/pakerfeldt/android-viewflow

 




벌써 3번째 오픈 소스 사용이니 파일 복사 부분은 간략히만 짚고 넘어가겠습니다.




res\values\attrs.xml 내용 복사

    <declare-styleable name="ViewFlow">
        <attr name="sidebuffer" format="integer" />
    </declare-styleable>
    <declare-styleable name="CircleFlowIndicator">
        <attr name="activeColor" format="color" />
        <attr name="inactiveColor" format="color" />
        <attr name="radius" format="dimension" />
		<attr name="centered" format="boolean" />
		<attr name="fadeOut" format="integer" />
		<attr name="inactiveType">
			<flag name="stroke" value="0" />
			<flag name="fill" value="1" />
		</attr>
		<attr name="activeType">
			<flag name="stroke" value="0" />
			<flag name="fill" value="1" />
		</attr>
    </declare-styleable>    
    <declare-styleable name="TitleFlowIndicator">
        <attr name="titlePadding" format="dimension" />
        <!-- Left/right padding of not active view titles. -->
        <attr name="clipPadding" format="dimension" />
        <attr name="selectedColor" format="color" />
        <attr name="selectedBold" format="boolean" />
        <attr name="selectedSize" format="dimension" />
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
        <attr name="footerLineHeight" format="dimension" />
        <attr name="footerColor" format="color" />
        <attr name="footerTriangleHeight" format="dimension" />
        <attr name="customTypeface" format="string" />
    </declare-styleable>


프로젝트에 org.taptwo.android.widget 패키지 추가


src\org\taptwo\android\widget\ 파일들 전체 복사


p.s) 소스파일들을 추가 하시게 되면, 소스에 오류가 나타납니다. 패키지 명이 달라서 나타나는 증상이니, 

import org.taptwo.android.widget.viewflow.R; 이분을 제거 하고 다시 패키지명.R을 임포트 하시면 됩니다. (간단하게 이클립스에서 Ctrl + Shift + O 한번  하시면 됩니다.)




그럼 이것으로 오픈 소스를 사용할 준비는 완료되었으니, 기존의 소스들을 수정 하도록 하겠습니다.


1. activity_main.xml 파일 수정


- activity_main.xml -

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/blog.example.androidui"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    
    <com.markupartist.android.widget.ActionBar
        android:id="@+id/actionbar"
        style="@style/ActionBar" />

    
    <org.taptwo.android.widget.ViewFlow
        android:id="@+id/viewflow"
		android:duplicateParentState="true" 
		android:layout_width="fill_parent" 
		android:layout_height="0dip"
		android:layout_weight="1">
	    
	</org.taptwo.android.widget.ViewFlow>
     <View
        android:layout_width="fill_parent"
        android:layout_height="2dip"
        android:background="#FFFF0000"
        />
    <LinearLayout android:layout_width="fill_parent"
		android:gravity="center_horizontal" android:id="@+id/header_layout"
		android:orientation="vertical" 
		android:layout_height="wrap_content">
		
        <org.taptwo.android.widget.TitleFlowIndicator
			android:id="@+id/viewflowindic" 
			android:layout_height="48dip"
			android:layout_width="fill_parent"
			app:footerLineHeight="2dp"
			app:footerTriangleHeight="10dp" 
			app:textColor="#A0FF0000" 
			app:selectedColor="#FFFF0000" 
			app:footerColor="#FFFF0000" 
			app:titlePadding="10dp" 
			app:textSize="17dp" app:selectedSize="18dp" 
			android:layout_marginTop="10dip" 
			app:clipPadding="5dp">
		</org.taptwo.android.widget.TitleFlowIndicator>

	</LinearLayout>

</LinearLayout>


우선 Action Bar 아래에 나오던 기본 뷰들을 전부 지웠구요, 그 부분에 viewflow 와 TitleFlowIndicator가 나타나도록 수정하였고, 뷰들의 속성 값들을 수정하였습니다.

 

자 그런데 여기서 TitleFlowIndicator 속성을 보시게되면 app: 이라는 접두어가 붙은 속성들이 등장하게됩니다. 

이 android-viewflow 오픈 소스는 app 이라는 xml 네임스페이스를 추가하여 view들의 속성을 지정하고있는데요. 이에 따라 android-viewflow 의 객체를 포함하는 최상위 view는 (여기선 LinearLayout 가 되겠지요.)  'xmlns' 라는 속성값을 통해 app 이라는 네임스페이스를 아래와 같이 추가 해주어야 합니다. 


xmls:app = "http://schemas.android.com/apk/res/프로젝트 패키지명"


p.s) 그런데 네임스페이스라고 보는게 옳은건지 잘모르겠네요;; 그저 이해를 돕기 위해 쓴 말이라고 생각하시면 될 것같습니다. 




2. default.xml 파일 추가


-default.xml-

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
    
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_marginLeft="16dip"
        android:layout_marginRight="16dip"
        android:layout_marginTop="30dip"
        android:layout_weight="1"
        android:orientation="vertical" >

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dip"
            android:text="@string/session"
            android:textColor="#000000"
            android:textSize="22sp" />

        <View
            android:layout_width="fill_parent"
            android:layout_height="1dip"
            android:background="#000000" />

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="48dip"
            android:baselineAligned="true" >

            <EditText
                android:layout_width="0dip"
                android:layout_height="wrap_content"
                android:layout_marginBottom="4dip"
                android:layout_marginRight="4dip"
                android:layout_marginTop="4dip"
                android:layout_weight="1"
                android:hint="@string/hint" />

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dip"
                android:contentDescription="@string/modify"
                android:src="@drawable/content_edit" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="48dip" >

            <EditText
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="4dip"
                android:layout_marginRight="4dip"
                android:layout_marginTop="4dip"
                android:hint="@string/hint" />
        </LinearLayout>

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dip"
            android:text="@string/session"
            android:textColor="#000000"
            android:textSize="22sp" />

        <View
            android:layout_width="fill_parent"
            android:layout_height="1dip"
            android:background="#000000" />

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="48dip"
            android:baselineAligned="true" >

            <Spinner
                android:id="@+id/androidui_sp_1"
                android:layout_width="0dip"
                android:layout_height="wrap_content"
                android:layout_marginBottom="4dip"
                android:layout_marginRight="4dip"
                android:layout_marginTop="4dip"
                android:layout_weight="1" />

            <Spinner
                android:id="@+id/androidui_sp_2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="4dip"
                android:layout_marginLeft="4dip"
                android:layout_marginTop="4dip" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="48dip"
            android:baselineAligned="true" >

            <EditText
                android:layout_width="0dip"
                android:layout_height="wrap_content"
                android:layout_marginBottom="4dip"
                android:layout_marginRight="4dip"
                android:layout_marginTop="4dip"
                android:layout_weight="1"
                android:hint="@string/hint" />

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="4dip"
                android:layout_marginLeft="4dip"
                android:layout_marginTop="4dip"
                android:text="@string/send" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>


별거 없습니다. 그냥 기존 activity_main.xml 에있던 기존 뷰들로 새로운 xml 파일을 하나 더 만들었습니다. 



3. activity_list.xml 파일 수정

- activity_list.xml -

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    
    <com.markupartist.android.widget.PullToRefreshListView
        android:id="@+id/android:list"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent"
        android:cacheColorHint ="#00000000"
        />
	
</LinearLayout>


이것도 별거 없습니다. 기존 소스에선 ActionBar와 Textview로 만들어진 네이게이션바를 빼고, 속성을 몇가지 수정한게 전부입니다. 



4. ViewFlowAdapter 구현


viewflow 클래스는 listview 처럼 Adapter를 통하여 데이터 및 뷰를 보여주고있습니다. 이러한 이유때문에 activity_main.xml 파일 외에 두개의 xml 파일이 추가적으로 필요하여, default.xml 파일을 새로 만들고, activity_list.xml 파일을 수정하였습니다. 



또한 TitleFlowIndicator 클래스는 TitleProvider 인터페이스를 구현한 클래스로 부터 데이터를 읽어들이는데 ViewFlowAdapter 클래스가 TitleProvider 인터페이스까지 구현하도록 하겠습니다.


- ViewFlowAdapter.java -

package blog.example.androidui;

import org.taptwo.android.widget.TitleProvider;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

public class ViewFlowAdapter extends BaseAdapter implements TitleProvider {

	private LayoutInflater mInflater;
	
	private String[] titles = {"View1", "View2"};    //타이틀
	
	public ViewFlowAdapter(Context context){
		mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	}
	
	
	@Override
	public int getCount() {
		return titles.length;    //아이템 갯수.
	}

	@Override
	public Object getItem(int position) {
		return position;
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

         /* 포지션에 따라 생성하는 view를 다르게 설정 */
 
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if(convertView == null){
			if(position == 0){	//기본뷰
				convertView = mInflater.inflate(R.layout.default_view, null);
				
			}else{	//리스트뷰
				convertView = mInflater.inflate(R.layout.activity_list, null);
			}
		}
		
		return convertView;
	}

	@Override
	public String getTitle(int position) {    //타이틀 값.
		return titles[position];
	}

}


ViewflowAdapter 클래스는 BaseAdapter를 상속받아 만든 클래스이기 때문에 특별히 따로 설명드릴만한건 없는것 같습니다. 

다만 getView 메소드에서 포지션값에 따라 view가 다르게 생성되는 부분만 유의 하시면 될것같습니다. 



4. MainActivity 수정


- MainActivity.java -

package blog.example.androidui;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Vector;

import org.taptwo.android.widget.TitleFlowIndicator;
import org.taptwo.android.widget.ViewFlow;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

import com.markupartist.android.widget.ActionBar;
import com.markupartist.android.widget.ActionBar.Action;
import com.markupartist.android.widget.ActionBar.IntentAction;
import com.markupartist.android.widget.PullToRefreshListView;
import com.markupartist.android.widget.PullToRefreshListView.OnRefreshListener;

public class MainActivity extends Activity {

	private ViewFlow viewFlow;  //오픈 소스 객체.

	
	//listview 관련
	private PullToRefreshListView list = null;
	
	private String[] mStrings = {
            "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam",
            "Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis",
            "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
            "Allgauer Emmentaler"};
	private LinkedList<String> mListItems;
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final ActionBar actionBar = (ActionBar) findViewById(R.id.actionbar);
        actionBar.setTitle("Home");
        
        final Action shareAction = new IntentAction(this, createShareIntent(), R.drawable.ic_title_share_default);
        actionBar.addAction(shareAction);
        
        //ListActivity를 실행하는 Action 주석 처리.
        //Intent intent = new Intent(this, ListViewActivity.class);
        //intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        
        //final Action otherAction = new IntentAction(this, intent, R.drawable.ic_title_export_default);
        //actionBar.addAction(otherAction);  
        
        
        viewFlow = (ViewFlow) findViewById(R.id.viewflow);
        ViewFlowAdapter adapter = new ViewFlowAdapter(this); //adapter 객체 생성
        viewFlow.setAdapter(adapter); //adapter 설정
        
        TitleFlowIndicator indicator = (TitleFlowIndicator) findViewById(R.id.viewflowindic);
		indicator.setTitleProvider(adapter); // title provider 설정
		viewFlow.setFlowIndicator(indicator); //viewflow 객체에 titleflowindicator 설정.
        
	// 기존  MainActivity 소스  	
        Spinner sp1 = (Spinner)viewFlow.findViewById(R.id.androidui_sp_1);
        Spinner sp2 = (Spinner)viewFlow.findViewById(R.id.androidui_sp_2);
        
        Vector<String> data1 = new Vector<String>();
        Vector<String> data2 = new Vector<String>();
        
        data1.add("2011-12-25");
        data2.add("8:00 am");
        
        ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(
				this,
				android.R.layout.simple_spinner_item,
				data1);

        adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		sp1.setAdapter(adapter1);
		
		ArrayAdapter<String> adapter2 = new ArrayAdapter<String>(
				this,
				android.R.layout.simple_spinner_item,
				data2);

		adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		sp2.setAdapter(adapter2);
        
		
		// 기존 ListActivity 소스 
		list = (PullToRefreshListView)viewFlow.findViewById(android.R.id.list); //  list의 id값이 android의 list 값이기 때문에 androi.R 을 참조.
		
		mListItems = new LinkedList<String>();
        mListItems.addAll(Arrays.asList(mStrings));
        
        ArrayAdapter<String> listadapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, mListItems);

        list.setAdapter(listadapter);
		
		
		list.setOnRefreshListener(new OnRefreshListener() {
            public void onRefresh() {
                // Do work to refresh the list here.
                new GetDataTask().execute();
            }
        });
    }

	private class GetDataTask extends AsyncTask<Void, Void, String[]> {

        protected String[] doInBackground(Void... params) {
            // Simulates a background job.
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                ;
            }
            return mStrings;
        }

        protected void onPostExecute(String[] result) {
            mListItems.addFirst("Added after refresh...");

            // Call onRefreshComplete when the list has been refreshed.
            if(list != null) list.onRefreshComplete();

            super.onPostExecute(result);
        }
    }
	
	
	/* If your min SDK version is < 8 you need to trigger the onConfigurationChanged in ViewFlow manually, like this */
	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		if(viewFlow != null) viewFlow.onConfigurationChanged(newConfig);	//Activity 환경설정 변경 
		super.onConfigurationChanged(newConfig);
	}
	
    public static Intent createIntent(Context context) {
        Intent i = new Intent(context, MainActivity.class);
        i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        return i;
    }
    
    private Intent createShareIntent() {
        final Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("text/plain");
        intent.putExtra(Intent.EXTRA_TEXT, "Shared from the ActionBar widget.");
        return Intent.createChooser(intent, "Share");
    }
}


실행 결과















기본 뷰에서 보내기 버튼 옆에있는 EditText를 선택하면 키보드가 올라오면서 선택된 EditText가 보이지 않는 버그가 있긴하지만 최종 목표를 달성하였기 때문에 쿨하게 무시하도록 하겠습니다. (나란 남자 이런남자)


여기까지 Android UI 만들기 포스팅이었구요, 무한 테클 및 질문 환영합니다. 감사합니다. 



AndroidUI - 최종.zip






posted by 엡뽀