Android 2013. 8. 22. 13:34

한 화면을 여러 공간으로 쪼개어 사용하거나 액티비티 이동없이 화면 내부구성을 다른 레이아웃으로 


교체 시킬 때 등 Fragment는 여러 군데에서 사용되어지고 있습니다.


Fragment 란 ? : http://androidhuman.tistory.com/469



Fragment를 사용하려면 그것을 사용하는 상위 activity가 FragmentActivity 여야 합니다.






Fragment 생명주기는 activity와 흡사하며 주의 할 점이 activity에서 this로 쓰던 부분을 getActivity()로 


바꿔 써주셔야 합니다.



큰 구성은 FragmentActivity를 상속받은 MainActivity.java 가 있고 Fragment 를 상속받은


OneFragment, TwoFragment, ThreeFragment.java 세가지 클래스를 만든다음 


activity_main.xml에 있는 ll_fragment 영역에 위에만든 세가지 fragment 를 삽입하는 방식입니다.



먼저 MainActivity 구성입니다.



위 그림과 같이 activity_main.xml  레이아웃 하나에 fragment 가 교체 될 영역과 그 영역을 컨트롤 할 


메뉴 뷰 를 만들어 줍니다. 



<MainActivity>

public class MainActivity extends FragmentActivity implements OnClickListener {

	final String TAG = "MainActivity";

	int mCurrentFragmentIndex;
	public final static int FRAGMENT_ONE = 0;
	public final static int FRAGMENT_TWO = 1;
	public final static int FRAGMENT_THREE = 2;

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

		Button bt_oneFragment = (Button) findViewById(R.id.bt_oneFragment);
		bt_oneFragment.setOnClickListener(this);
		Button bt_twoFragment = (Button) findViewById(R.id.bt_twoFragment);
		bt_twoFragment.setOnClickListener(this);
		Button bt_threeFragment = (Button) findViewById(R.id.bt_threeFragment);
		bt_threeFragment.setOnClickListener(this);

		mCurrentFragmentIndex = FRAGMENT_ONE;

		fragmentReplace(mCurrentFragmentIndex);
	}

	public void fragmentReplace(int reqNewFragmentIndex) {

		Fragment newFragment = null;

		Log.d(TAG, "fragmentReplace " + reqNewFragmentIndex);

		newFragment = getFragment(reqNewFragmentIndex);

		// replace fragment
		final FragmentTransaction transaction = getSupportFragmentManager()
				.beginTransaction();

		transaction.replace(R.id.ll_fragment, newFragment);

		// Commit the transaction
		transaction.commit();

	}

	private Fragment getFragment(int idx) {
		Fragment newFragment = null;

		switch (idx) {
		case FRAGMENT_ONE:
			newFragment = new OneFragment();
			break;
		case FRAGMENT_TWO:
			newFragment = new TwoFragment();
			break;
		case FRAGMENT_THREE:
			newFragment = new ThreeFragment();
			break;

		default:
			Log.d(TAG, "Unhandle case");
			break;
		}

		return newFragment;
	}

	@Override
	public void onClick(View v) {

		switch (v.getId()) {

		case R.id.bt_oneFragment:
			mCurrentFragmentIndex = FRAGMENT_ONE;
			fragmentReplace(mCurrentFragmentIndex);
			break;
		case R.id.bt_twoFragment:
			mCurrentFragmentIndex = FRAGMENT_TWO;
			fragmentReplace(mCurrentFragmentIndex);
			break;
		case R.id.bt_threeFragment:
			mCurrentFragmentIndex = FRAGMENT_THREE;
			fragmentReplace(mCurrentFragmentIndex);
			break;

		}

	}

}



전체적으로 보면 fragment 마다 int으로된 구분자를 주고 메뉴 버튼이 클릭 될 때마다 해당 fragment의 


index를 mCurrentFragmentIndex 라는 int 형 변수에 담아 fragment 교체 매소드를 태우는 식입니다.



onCreate부터 보면 메뉴 버튼등록과 리스너를 달고 mCurrentFragmentIndex에 첫 화면 fragment인 


FRAGMENT_ONE 를 넣어주고 fragmentReplace 매소드를 타게됩니다.


fragmentReplace 매소드에는 넘겨받은 fragment index를 가지고 getFragment 매소드를 가서 


실질적인 fragment의 객체 생성을 하고 그 객체를 리턴해줍니다. 


다시 fragmentReplace매소드로 돌아와 리턴받은 fragment로 ll_fragment 라는 레이아웃 영역에


교체를 하게 됩니다. 다른 버튼을 눌렀을 때도 이와 같은 방식으로 동작하게 됩니다. 



Fragment 들은 

public class OneFragment extends Fragment implements OnClickListener {

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

		View v = inflater.inflate(R.layout.onefragment, container, false);

		Button button = (Button) v.findViewById(R.id.bt_ok);
		button.setOnClickListener(this);

		return v;
	}

	@Override
	public void onClick(View v) {

		switch (v.getId()) {

		case R.id.bt_ok:
			Toast.makeText(getActivity(), "OneFragment", Toast.LENGTH_SHORT)
					.show();
			break;

		}

	}

}

이처럼 Activity와 비슷한 구조이고 



마지막으로 activity_main.xml 구조입니다.


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

    android:layout_width="match_parent"

    android:layout_height="match_parent" >


    <LinearLayout

        android:layout_width="fill_parent"

        android:layout_height="fill_parent"

        android:background="#ffffffff"

        android:orientation="vertical" >


        <LinearLayout

            android:id="@+id/ll_fragment"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:layout_weight="1" />


        <LinearLayout

            android:layout_width="fill_parent"

            android:layout_height="wrap_content"

            android:layout_marginBottom="10dp"

            android:layout_marginTop="10dp"

            android:background="#ffffffff"

            android:orientation="horizontal" >


            <Button

                android:id="@+id/bt_oneFragment"

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:layout_weight="1"

                android:text="1" />


            <Button

                android:id="@+id/bt_twoFragment"

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:layout_weight="1"

                android:text="2" />


            <Button

                android:id="@+id/bt_threeFragment"

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:layout_weight="1"

                android:text="3" />

        </LinearLayout>

    </LinearLayout>


</RelativeLayout>



ll_fragment 라는 영역이 Fragment들이 교체 될 영역입니다.








FragmentExample.zip



posted by 젊은쎄오
:
Android 2013. 8. 19. 11:24

페이스북 앱을 보면 좌측 메뉴 버튼을 눌렀을 때 좌측에서 슬라이드로 메뉴가 나오게 됩니다. 


원리는 메인화면을 구성하는 xml에 전체를 FrameLayout으로 감싸고 내부에 메뉴 레이아웃을 깔고 


그 위에 메인 화면의 레이아웃을 포개어 놓습니다. 그러고 메인화면의 메뉴 버튼을 눌렀을 때 메인화면 레이아웃을


애니메이션 처리하여 우측으로 밀어버려 아래에있던 메뉴 레이아웃이 노출되는 식입니다. 







<activity_main.xml>




구성요소는 다음과 같습니다.


1. MainActivity.java

2. OpenAnimation.java

3. CloseAnimation.java

4. activity_main.xml

5. leftslidemenu.xml



먼저 MainActivity 입니다. 



public class MainActivity extends Activity implements OnClickListener {

	// slide menu 
	private DisplayMetrics metrics;
	private LinearLayout ll_mainLayout;
	private LinearLayout ll_menuLayout;
	private FrameLayout.LayoutParams leftMenuLayoutPrams;
	private int leftMenuWidth;
	private static boolean isLeftExpanded;

	private Button bt_left, btn1, btn2, btn3, btn4;

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

		initSildeMenu();

	}

	private void initSildeMenu() {

		// init left menu width
		metrics = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(metrics);
		leftMenuWidth = (int) ((metrics.widthPixels) * 0.75);

		// init main view
		ll_mainLayout = (LinearLayout) findViewById(R.id.ll_mainlayout);

		// init left menu
                ll_menuLayout = (LinearLayout) findViewById(R.id.ll_menuLayout);
		leftMenuLayoutPrams = (FrameLayout.LayoutParams) ll_menuLayout
				.getLayoutParams();
		leftMenuLayoutPrams.width = leftMenuWidth;
		ll_menuLayout.setLayoutParams(leftMenuLayoutPrams);

		// init ui
		bt_left = (Button) findViewById(R.id.bt_left);
		bt_left.setOnClickListener(this);

		btn1 = (Button) findViewById(R.id.btn1);
		btn2 = (Button) findViewById(R.id.btn2);
		btn3 = (Button) findViewById(R.id.btn3);
		btn4 = (Button) findViewById(R.id.btn4);
		btn1.setOnClickListener(this);
		btn2.setOnClickListener(this);
		btn3.setOnClickListener(this);
		btn4.setOnClickListener(this);
	}

	/**
	 * left menu toggle
	 */
	private void menuLeftSlideAnimationToggle() {

		if (!isLeftExpanded) {

			isLeftExpanded = true;

			// Expand
			new OpenAnimation(ll_mainLayout, leftMenuWidth,
					Animation.RELATIVE_TO_SELF, 0.0f,
					Animation.RELATIVE_TO_SELF, 0.75f, 0, 0.0f, 0, 0.0f);

			// enable all of menu view
			FrameLayout viewGroup = (FrameLayout) findViewById(R.id.ll_menuLayout)
					.getParent();
			enableDisableViewGroup(viewGroup, true);

			// enable empty view
			((LinearLayout) findViewById(R.id.ll_empty))
					.setVisibility(View.VISIBLE);

			findViewById(R.id.ll_empty).setEnabled(true);
			findViewById(R.id.ll_empty).setOnTouchListener(
					new OnTouchListener() {

						@Override
						public boolean onTouch(View arg0, MotionEvent arg1) {
							menuLeftSlideAnimationToggle();
							return true;
						}
					});

		} else {
			isLeftExpanded = false;

			// Collapse
			new CloseAnimation(ll_mainLayout, leftMenuWidth,
					TranslateAnimation.RELATIVE_TO_SELF, 0.75f,
					TranslateAnimation.RELATIVE_TO_SELF, 0.0f, 0, 0.0f, 0, 0.0f);

			
			// enable all of menu view
			FrameLayout viewGroup = (FrameLayout) findViewById(R.id.ll_menuLayout)
					.getParent();
			enableDisableViewGroup(viewGroup, false);

			// disable empty view
			((LinearLayout) findViewById(R.id.ll_empty))
					.setVisibility(View.GONE);
			findViewById(R.id.ll_empty).setEnabled(false);

		}
	}


    public static void enableDisableViewGroup(ViewGroup viewGroup,
			boolean enabled) {
		int childCount = viewGroup.getChildCount();
		for (int i = 0; i < childCount; i++) {


			View view = viewGroup.getChildAt(i);
			view.setEnabled(enabled);

			if (view instanceof ViewGroup) {
				enableDisableViewGroup((ViewGroup) view, enabled);
			}
		}
    }


	@Override
	public void onClick(View v) {

		switch (v.getId()) {
		case R.id.bt_left:
			menuLeftSlideAnimationToggle();
			break;
		case R.id.btn1:
			Toast.makeText(getApplicationContext(), "1", Toast.LENGTH_SHORT)
					.show();
			break;
		case R.id.btn2:
			Toast.makeText(getApplicationContext(), "2", Toast.LENGTH_SHORT)
					.show();
			break;
		case R.id.btn3:
			Toast.makeText(getApplicationContext(), "3", Toast.LENGTH_SHORT)
					.show();
			break;
		case R.id.btn4:
			Toast.makeText(getApplicationContext(), "4", Toast.LENGTH_SHORT)
					.show();
			break;

		}

	}
}


최초에 initSildeMenu()매소드에서 사용 할 메인 레이아웃과 메뉴 레이아웃을 초기화 합니다.


메뉴 레이아웃은 열렸을 때 화면 전체너비로 열리는 것이 아니라 우측에 메인 레이아웃이 살짝 걸치므로 


메뉴 레아이웃 너비가 될 leftMenuWidth 변수에 화면전체 너비에 0.75 정도 비율을 넣어 줍니다. 






이 후에 메인 레이아웃을 findViewById 하고 메뉴 레이아웃도 findViewById 한 후 


아까 만든 0.75의 너비를 width로 넣어줍니다. 나머지 버튼들도 리스너를 달아줍니다. 




이제 핵심이 되는 menuLeftSlideAnimationToggle() 매소드 입니다


토글이라는 말과 같이 메뉴가 열고 닫힐 때 모두 이 매소드를 사용하며


isLeftExpanded라는 boolean 값으로 열고 닫힐 때를 판단하게 됩니다.


우선 열릴 때 입니다.


메뉴버튼을 눌러 메뉴가 열리게 되면 우선 isLeftExpanded = true; 를 줘서 메뉴가 열렸다를 저장하고


OpenAnimation 이란 클래스를 통하여 메뉴가 열리게 됩니다.


처음 구조 설명 할 때 설명을 안했지만 메인 레이아웃 위에 empty 레이아웃을 하나 더 포개었는데


이 뷰를 VISIBLE 시키게 됩니다. 이 ll_empty 뷰는 투명한 뷰이며 


메뉴가 열려있을 때 우측에 살짝 보이는 메인 레이아웃을 덮고 있습니다. 


이 뷰를 터치하면 menuLeftSlideAnimationToggle(); 매소드를 호출하여 메뉴가 닫히게 됩니다. 


이는 꼭 메뉴버튼을 눌러야 메뉴가 닫히는것이 아닌 그냥 메인 뷰만 터치하면 닫히도록하는 편리함을 위함입니다.




public class OpenAnimation extends TranslateAnimation implements
		Animation.AnimationListener {

	private LinearLayout mainLayout
	int panelWidth;

	public OpenAnimation(LinearLayout layout, int width, int fromXType,
			float fromXValue, int toXType, float toXValue, int fromYType,
			float fromYValue, int toYType, float toYValue) {

		super(fromXType, fromXValue, toXType, toXValue, fromYType, fromYValue,
				toYType, toYValue);

		// init
		mainLayout = layout;
		panelWidth = width;
		setDuration(250);
		setFillAfter(false);
		setInterpolator(new AccelerateDecelerateInterpolator());
		setAnimationListener(this);
		mainLayout.startAnimation(this);
	}

	public void onAnimationEnd(Animation arg0) {

		LayoutParams params = (LayoutParams) mainLayout.getLayoutParams();
		params.leftMargin = panelWidth;
		params.gravity = Gravity.LEFT;
		mainLayout.clearAnimation();
		mainLayout.setLayoutParams(params);
		mainLayout.requestLayout();

	}

	public void onAnimationRepeat(Animation arg0) {

	}

	public void onAnimationStart(Animation arg0) {

	}

}


OpenAnimation 클래스입니다. 받아온 속성들로 초기화를 하며 setDuration(250);이 부분이 열릴 때의 속도입니다.



메뉴가 닫힐 때는 isLeftExpanded로 닫힐 때를 가려내고 isLeftExpanded = flase;를 줘 닫힘을 저장합니다.


OpenAnimarion 클래스와 같이 CloseAnimation을 호출하게되고 활성화 되어있던 empty뷰를 꺼서 


메인 레이아웃이 다시 노출되게 합니다.

public class CloseAnimation extends TranslateAnimation implements
		TranslateAnimation.AnimationListener {

	private LinearLayout mainLayout;
	int panelWidth;

	public CloseAnimation(LinearLayout layout, int width, int fromXType,
			float fromXValue, int toXType, float toXValue, int fromYType,
			float fromYValue, int toYType, float toYValue) {

		super(fromXType, fromXValue, toXType, toXValue, fromYType, fromYValue,
				toYType, toYValue);

		// Initialize
		mainLayout = layout;
		panelWidth = width;
		setDuration(250);
		setFillAfter(false);
		setInterpolator(new AccelerateDecelerateInterpolator());
		setAnimationListener(this);

		// Clear left and right margins
		LayoutParams params = (LayoutParams) mainLayout.getLayoutParams();
		params.rightMargin = 0;
		params.leftMargin = 0;
		mainLayout.setLayoutParams(params);
		mainLayout.requestLayout();
		mainLayout.startAnimation(this);

	}

	public void onAnimationEnd(Animation animation) {

	}

	public void onAnimationRepeat(Animation animation) {

	}

	public void onAnimationStart(Animation animation) {

	}

}


CloseAnimation입니다. 



아래엔 사용한 xml 파일입니다.


<-- activity_main.xml -->


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

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="fill_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >


    <FrameLayout

        android:layout_width="fill_parent"

        android:layout_height="match_parent"

        android:background="#ffffff"

        android:orientation="vertical" >


        <LinearLayout

            android:id="@+id/ll_menuLayout"

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:background="#ff393c46"

            android:gravity="left"

            android:orientation="vertical"

            android:textColor="#ff000000" >


            <!-- include -->


            <include

                android:id="@+id/ic_leftslidemenu"

                android:layout_width="match_parent"

                android:layout_height="match_parent"

                layout="@layout/leftslidemenu" />

        </LinearLayout>


        <!-- slide layout -->


        <LinearLayout

            android:id="@+id/ll_mainlayout"

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:background="#ffffffff"

            android:gravity="left"

            android:orientation="vertical" >


            <FrameLayout

                android:layout_width="match_parent"

                android:layout_height="match_parent" >


                <Button

                    android:id="@+id/bt_left"

                    android:layout_width="wrap_content"

                    android:layout_height="wrap_content"

                    android:layout_margin="10dp"

                    android:text="Left Slide" />


                <LinearLayout

                    android:id="@+id/ll_fragment"

                    android:layout_width="match_parent"

                    android:layout_height="wrap_content" >

                </LinearLayout>


                <LinearLayout

                    android:id="@+id/ll_empty"

                    android:layout_width="fill_parent"

                    android:layout_height="fill_parent"

                    android:background="@android:color/transparent" />

            </FrameLayout>

        </LinearLayout>

    </FrameLayout>


</LinearLayout>





<-- leftslidemenu.xml -->


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

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="fill_parent"

    android:layout_height="match_parent"

    android:background="#994444cc"

    android:orientation="vertical"

    android:padding="10dp" >


    <Button

        android:id="@+id/btn1"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:text="Button 1 " />


    <Button

        android:id="@+id/btn2"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:text="Button 2 " />


    <Button

        android:id="@+id/btn3"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:text="Button 3 " />


    <Button

        android:id="@+id/btn4"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:text="Button 4 " />


</LinearLayout>



위 처럼 메인 레이아웃에 메뉴 레이아웃이 include 되어있습니다. 




-- 2013 10 04 내용추가 


현재 버전에는 메인 화면에서는 메인 화면이 덮고있는 중에도 터치를 하면 뒤 LeftMenu의 버튼이 눌리게 됩니다. 


초보개발자 님 말씀대로 visible방법도 가능하고 


저는 뷰를 닫을 때 LeftMenu에 있는 모든 뷰를 다 setEnable로 꺼버리고 


열 때는 켜는 식을 사용 했습니다. 


열고 닫을 때 


FrameLayout viewGroup = (FrameLayout) findViewById(R.id.ll_menuLayout)

.getParent();


이런식으로 menu  레이아웃을 잡고 


enableDisableViewGroup(viewGroup, false);


setEnable을 true 할 건지 false 할 건지 정해 주시면 됩니다. 


그럼 


 public static void enableDisableViewGroup(ViewGroup viewGroup,
            boolean enabled) {
        int childCount = viewGroup.getChildCount();
        for (int i = 0; i < childCount; i++) {
 
 
            View view = viewGroup.getChildAt(i);
            view.setEnabled(enabled);
 
            if (view instanceof ViewGroup) {
                enableDisableViewGroup((ViewGroup) view, enabled);
            }
        }
  }
 

menu레이아웃에 있는 뷰들의 갯수 만큼 for문을 돌며 각각 setEnable을 할 수 있습니다.



응용하면 좌측 슬라이드 메뉴를 열었을 때 좌측 슬라이드에 있는 다른 버튼은 눌리지 않고

LeftSlide 버튼만 눌리게 하고싶을 땐

FrameLayout viewGroup = (FrameLayout) findViewById(R.id.ll_fragment)

.getParent();


이렇게 좌측 슬라이드 메뉴 레이아웃을 잡고 


enableDisableViewGroup 매소드 for문 안쪽에


View view = viewGroup.getChildAt(i);


if (view.getId() != R.id.ib_left) {

view.setEnabled(enabled);

if (view instanceof ViewGroup) {

enableDisableViewGroup((ViewGroup) view, enabled);

}

}


이런식으로 LeftSlide 버튼만 살려서 disable을 막는 방법이 있습니다.











LeftSlideMenuExample.zip









-- 2013 10 22 내용추가 


좌 우 측 슬라이드가 모두 되는 소스 첨부 합니다. 


우측 슬라이드도 좌측 슬라이드와 같은 방식으로 만들었습니다. 





SlideMenu.zip








posted by 젊은쎄오
:
Android 2013. 7. 26. 10:27

프로젝트를 진행하다보면 거의 모든 매소드에 로그를 사용하는데 


개발중엔 상관없지만 배포 할 때 로그를 안나오게 하는것이 바람직합니다.


이 때, 수 십개의 클래스를 돌아다니며 로그를 지우는 것도 일이지만 


만약 배포 후 버그가 나온다면 지웠던 로그를 다시 작성해야 되는 일이 생깁니다.


이런 불상사를 방지하는 방법이 로그 클래스를 만들어 관리를 해주는 것입니다. 


public class RbLog {

	/** Disable Debug */
	// public static boolean D = false;

	/** Enable Debug */
	public static final boolean D = true;

	public static void d(String tag, String msg) {
		if (D && tag != null && msg != null)
			Log.d(tag, msg);
	}

	public static void d(Activity activity, String msg) {
		if (D && activity != null && msg != null)
			Log.d(activity.getLocalClassName(), msg);

	}
}

위와 같이 d 라는 로그찍는 매소드를 만들어 static으로 선언하게 되면 



RbLog.d(TAG, "Log Test");


이렇게 로그형식으로 똑같이 사용 할 수 있게 됩니다.


개발을 완료하고 배포할 때 boolean형태의 D 변수를 false로 활성화시키면 


d 매소드 조건문에 의하여 if를 통과 못하게되고 모든 로그가 찍히지 않게됩니다. 


버그가 발견되어 다시 디버그를 하고싶을 땐 true로만 바꿔주면 곳곳에 심어놨던 로그들이


다시 찍히게 됩니다. 


추가로 i, e 등의 매소드도 만들어 사용 할 수 있습니다.

posted by 젊은쎄오
:
Android 2013. 7. 25. 16:09

프로젝트 진행 중에 어레이어답터를 커스텀으로 사용해야 할 일이 자주 생겨 


기본적으로 필요한 틀을 만들어 놓은것을 공유합니다. 






필요한 자바파일은 


- 리스트뷰가 있는 MainActivity 

- BaseAdapter를 상속 받고있는MainListAdapter

- 콘텐츠가 들어있는 ExamEntity 


필요한 xml 파일은 


- MainActivity의 Layout인 activity_main.xml 

- 리스트 row를 구성 할 item_row.xml


입니다. 


< MainActivity>

public class MainActivity extends Activity {

	private final String TAG = "MainActivity";

	// list
	private ListView lv_main;
	private ArrayList mExamItemArrayList;
	private MainListAdapter mMainListAdapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Log.d(TAG, "onCreate");

		viewInit();
		setContent();
	}

	/**
	 * init mainview
	 */
	private void viewInit() {

		Log.d(TAG, "viewInit");

		lv_main = (ListView) findViewById(R.id.lv_main);

		mExamItemArrayList = new ArrayList();
		mMainListAdapter = new MainListAdapter(getApplicationContext(),
				mExamItemArrayList, R.layout.item_row);

		lv_main.setAdapter(mMainListAdapter);

	}

	/**
	 * set content
	 */
	private void setContent() {
		Log.d(TAG, "setContent");

		for (int itemCount = 0; itemCount < 20; itemCount++) {
			ExamEntity mExamEntity = new ExamEntity();

			mExamEntity.title = "Title : " + itemCount;
			mExamEntity.content = "Content : " + itemCount;

			mExamItemArrayList.add(mExamEntity);

		}

		mMainListAdapter.notifyDataSetChanged();

	}

}


MainActivity에선 기본적은 어답터와 리스트뷰 연결 등의 초기 작업을 하고


setContent 매소드를 통하여 entity에 리스트 row에 뿌릴 콘텐츠를 저장합니다. 




<MainListAdapter>

public class MainListAdapter extends BaseAdapter {

	final String TAG = "MainListAdapter";

	public Context mContext;
	private ArrayList mExamItemArrayList;
	private LayoutInflater mLayoutInflater;
	int resource;

	public MainListAdapter(Context context,
			ArrayList mExamItemArrayList, int resource) {
		mContext = context;
		this.mExamItemArrayList = mExamItemArrayList;
		this.mLayoutInflater = (LayoutInflater) context
				.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		this.resource = resource;
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return mExamItemArrayList.size();
	}

	@Override
	public Object getItem(int arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public long getItemId(int arg0) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public View getView(final int position, View convertView, ViewGroup parent) {

		Holder holder = new Holder();
		if (convertView == null) {
			convertView = mLayoutInflater.inflate(resource, null);

			// find resource

			holder.tv_title = (TextView) convertView
					.findViewById(R.id.tv_title);
			holder.tv_content = (TextView) convertView
					.findViewById(R.id.tv_content);
			convertView.setTag(holder);

		} else {
			holder = (Holder) convertView.getTag();
		}

		// set content
		String title = mExamItemArrayList.get(position).title;
		String content = mExamItemArrayList.get(position).content;
		holder.tv_title.setText(title);
		holder.tv_content.setText(content);

		// set click listener
		holder.tv_title.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {

				Toast.makeText(mContext,
						mExamItemArrayList.get(position).title,
						Toast.LENGTH_SHORT).show();

			}
		});

		holder.tv_content.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				Toast.makeText(mContext,
						mExamItemArrayList.get(position).content,
						Toast.LENGTH_SHORT).show();

			}
		});

		return convertView;
	}

	private class Holder {
		TextView tv_title, tv_content;
	}

}

MainListAdapter에선, row에서 쓸 텍스트뷰를 선언하고 MainActivity에서 넘겨받은 


ArrayList를 이용하여 ExamEntity의 콘텐츠들을 꺼내어 쓰게됩니다.



<ExamEntity>

public class ExamEntity {

	public String title;
	public String content;

}

기본적인 entity입니다. setter와 getter를 추가 할 수 있습니다. 




위 기본틀을 붙여놓고 필요에 따라 리스트 row를 구성하는 row.xml을 수정하고 


그에 맞게 entity, holder, getView()매소드를 수정하여 원하는 리스트뷰를 구성 할 수 있습니다.







소스 첨부 합니다.




ListAdapterExample.zip









posted by 젊은쎄오
:
Android 2013. 7. 9. 17:48

자동 로그인 여부, 또는 설정에서 저장했던 값 등


앱이 종료되어도 보존되어야 하는 데이터를 저장할 때 


흔히 SharedPreferences를 사용한다. 


	SharedPreferences pref = mContext.getSharedPreferences(com.exam.pref,
				Activity.MODE_PRIVATE);
	SharedPreferences.Editor editor = pref.edit();
	editor.putString("key", value);
	editor.commit();


보통 이런식으로 사용하는데 이는 키 값을 수정 할 일이 있거나


찾을 일이 있을 때 따로 키 목록을 작성해 


놓은 곳이 없다면 나중에 관리가 힘들어지는 단점이 있다. 




그래서 아예  Preference 클래스를 하나 만들어 두고 그 클래스에 


int, String, boolean을 담고 꺼내는 getter, setter 매소드와 


사용하는 키 값을 모두 선언하여 


클래스에 점만 찍으면 키, 저장, 꺼내쓰기가 가능하도록 하였다. 



public class RbPreference {

	private final String PREF_NAME = "com.rabiaband.pref";

	public final static String PREF_INTRO_USER_AGREEMENT = "PREF_USER_AGREEMENT";
	public final static String PREF_MAIN_VALUE = "PREF_MAIN_VALUE";
	

	static Context mContext;

	public RbPreference(Context c) {
		mContext = c;
	}

	public void put(String key, String value) {
		SharedPreferences pref = mContext.getSharedPreferences(PREF_NAME,
				Activity.MODE_PRIVATE);
		SharedPreferences.Editor editor = pref.edit();

		editor.putString(key, value);
		editor.commit();
	}

	public void put(String key, boolean value) {
		SharedPreferences pref = mContext.getSharedPreferences(PREF_NAME,
				Activity.MODE_PRIVATE);
		SharedPreferences.Editor editor = pref.edit();

		editor.putBoolean(key, value);
		editor.commit();
	}

	public void put(String key, int value) {
		SharedPreferences pref = mContext.getSharedPreferences(PREF_NAME,
				Activity.MODE_PRIVATE);
		SharedPreferences.Editor editor = pref.edit();

		editor.putInt(key, value);
		editor.commit();
	}

	public String getValue(String key, String dftValue) {
		SharedPreferences pref = mContext.getSharedPreferences(PREF_NAME,
				Activity.MODE_PRIVATE);

		try {
			return pref.getString(key, dftValue);
		} catch (Exception e) {
			return dftValue;
		}

	}

	public int getValue(String key, int dftValue) {
		SharedPreferences pref = mContext.getSharedPreferences(PREF_NAME,
				Activity.MODE_PRIVATE);

		try {
			return pref.getInt(key, dftValue);
		} catch (Exception e) {
			return dftValue;
		}

	}

	public boolean getValue(String key, boolean dftValue) {
		SharedPreferences pref = mContext.getSharedPreferences(PREF_NAME,
				Activity.MODE_PRIVATE);

		try {
			return pref.getBoolean(key, dftValue);
		} catch (Exception e) {
			return dftValue;
		}
	}
}


위와 같이 상단에 각각 사용할 키를 선언하고 타입별로 같은 이름의 setter,getter 매소드를


만들어 놓으면 어디서든 위 클래스를 이용하여 해당키와 한가지 매소드로 원하는 작업 수행이 가능하다.



RbPreference pref = new RbPreference(this);

// set
pref.put(RbPreference.PREF_USER_AGREEMENT, true);

// get
pref.getValue(RbPreference.PREF_USER_AGREEMENT, false);


이런식으로 사용된다. 


posted by 젊은쎄오
:
Android 2013. 5. 6. 23:55

sns에서 자주보이는 현재 보여지는 글이 쓰여진 시점부터 


지금까지의 시간을 표현하는 방법이다. 


출처는 http://gubok.tistory.com/227 여기이고 아래는 소스이다. 



private static class TIME_MAXIMUM{
        public static final int SEC = 60;
        public static final int MIN = 60;
        public static final int HOUR = 24;
        public static final int DAY = 30;
        public static final int MONTH = 12;
}


public static String formatTimeString(Date tempDate) {

	long curTime = System.currentTimeMillis();
	long regTime = tempDate.getTime();
	long diffTime = (curTime - regTime) / 1000;

	String msg = null;
	if (diffTime < TIME_MAXIMUM.SEC) {
			// sec
		msg = "방금 전";
	} else if ((diffTime /= TIME_MAXIMUM.SEC) < TIME_MAXIMUM.MIN) {
		// min
		msg = diffTime + "분 전";
	} else if ((diffTime /= TIME_MAXIMUM.MIN) < TIME_MAXIMUM.HOUR) {
		// hour
		msg = (diffTime) + "시간 전";
	} else if ((diffTime /= TIME_MAXIMUM.HOUR) < TIME_MAXIMUM.DAY) {
		// day
			msg = (diffTime) + "일 전";
	} else if ((diffTime /= TIME_MAXIMUM.DAY) < TIME_MAXIMUM.MONTH) {
		// day
	        msg = (diffTime) + "달 전";
	} else {
		msg = (diffTime) + "년 전";
	}

	return msg;
}



이 부분만 있으면 구현 가능하다. 


그러나 나는 날짜가 yyyy-mm-dd hh:mm:ss format의 String형식으로  


넘어오고 있었기 때문에 이 String을 위와 같은 format의 Date 변수로 변환하는 부분이 필요했다.


String stringDate = "2013-05-06 14:42:00";
java.text.SimpleDateFormat format = new java.text.SimpleDateFormat(
			"yyyy-MM-dd HH:mm:ss");
java.util.Date date = format.parse(stringDate);


위와 같이 format 형태를 상황에 맞게 만들어 date형태로 변환해 준 다음 


파라미터로 넘겨주면 알맞은 리턴값이 돌아오게 된다. 



posted by 젊은쎄오
:
Android 2013. 5. 6. 23:23

일반적으로 안드로이드에서 텍스트의 컬러를 바꿀때 TextView를 잡아 컬러를 주는데 


이 방법의 단점은 TextView 전체가 한 가지 컬러로 바뀐다는 점이다.


그래서 SpannableStringBuilder를 사용하여  String 변수의 시작점과 끝점을 잡아 


사이값의 컬러를 바꿔줄수있다. 



 
String originText = "과일 상자에 수박을 담아가셨습니다.";
SpannableStringBuilder mSpannableStringBuilder = new SpannableStringBuilder(originText);
mSpannableStringBuilder.setSpan(new ForegroundColorSpan(0xFF04478f), 7,
					originText.length() - 10,
					Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textview.append(mSpannableStringBuilder);


setSpan(Object what, int start, int end, int flags) Mark the specified range of text with the specified object. 


API에는 이렇게 나와있다. 


각각 스타일, 시작점, 끝점, flag 인데 flag를 아직 잘 모르겠다. 구글링 해보니 앞뒤속성 관련 


값 같은데 바꿔도 결과가 같아서 자세히 알아보고 문서 수정보완 해야겠다.


첫번째 자리에 컬러 값이 아닌 


new AbsoluteSizeSpan(10) 이런식으로 넣으면 그 부분만 글자 크기가 달라진다. 


동시 적용하고 싶음 두 줄을 호출하면 된다. 


마지막으로 textview에 setText가 아닌 append 라는게 특이하다. 


결과는 


과일 상자에 수박을 담아가셨습니다.



posted by 젊은쎄오
:
Android 2013. 4. 15. 17:36

EditText 에서 사용하는 hint 속성 주는 방법입니다. 



- hint Color

 android:textColorHint="#ff000000" 


- hint Size ( hint 사이즈는 텍스트 사이즈를 따라갑니다.)

 android:textSize="12sp" 


posted by 젊은쎄오
:
Android 2013. 4. 10. 10:03

xml에서 뷰들에 색을 넣는 방법엔 


android:background="#ff0000ff" 이런식으로 사용합니다. 


이걸 자바 소스에서 사용하는 방법입니다.



 

 textview.setTextColor(0xFF0000ff); 


posted by 젊은쎄오
:
Android 2012. 10. 8. 17:47
안드로이드 모바일에서 이미 개발된 모바일 웹을 웹뷰로 띄워야 할 때가 있습니다. 


기본 모바일 웹 페이지만 띄우는 작업이면 상관이없는데 


 이 모바일 페이지에서 전화를 건다거나 동영상을 재생한다면 안드로이드는 동작을 안하게되죠 


지금 떠있는 것은 웹뷰이고 웹뷰는 동영상이나 전화거는 뷰가 아니기때문입니다. 


그래서 url을 가지고 동영상, 전화, 메일등을 가려내 해당 뷰로 넘겨주어 


 굳이 모바일 웹쪽을 수정하지않아도 앱단에서 해결 할 수 있는 방법입니다.

 
	private class HelloWebViewClient extends WebViewClient {
		@Override
		public boolean shouldOverrideUrlLoading(WebView view, String url) {
			String origin_url = url;
			String temp_url = origin_url.substring(origin_url.length() - 3,
					origin_url.length());

			if (temp_url.equals("mp4")) {
				
				// 동영상 플레이어로 재생하기 
				
				Intent i = new Intent(Intent.ACTION_VIEW);
				Uri uri = Uri.parse(url);
				i.setDataAndType(uri, "video/mp4");
				startActivity(i);
			} else if (origin_url.startsWith("tel:")) {

				// 전화 걸기 

				Intent call_phone = new Intent(Intent.ACTION_VIEW,
						Uri.parse(origin_url));
				// 현재의 activity 에 대해 startActivity 호출
				startActivity(call_phone);
				return true;

			} else if (origin_url.startsWith("mailto:")) {
				
				// 이메일 보내기 
				
				String email = origin_url.replace("mailto:", "");
				final Intent intent = new Intent(
						android.content.Intent.ACTION_SEND);
				intent.setType("plain/text");
				intent.putExtra(android.content.Intent.EXTRA_EMAIL,
						new String[] { email });
				intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "제목");
				intent.putExtra(android.content.Intent.EXTRA_TEXT, "내용");
				startActivity(Intent.createChooser(intent, "이메일 전송"));

			} else {
				
				// 기본 웹 페이지 넘어가기 
				
				view.loadUrl(url);
			}

			return true;
		}
	}



(2012 10 14 에 내용 추가 )


http://가 아닌 rtsp://로 시작하는 mp4 영상파일은 위 방법으로 재생이 되지않습니다. 

 
if (temp_url.endsWith("mp4")) {
				// 동영상 플레이어로 재생하기
				try {
					Intent i = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
					startActivity(i);
				} catch (URISyntaxException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}


이런 식으로 타입지정하는  i.setDataAndType(uri, "video/mp4"); 이부분을 빼주셔야 재생이 됩니다.





추가로 띄우는 웹 페이지의 크기가 클 때 전체화면에 딱 차게 뜨는 코드도 추가합니다.
 
mWebView = (WebView) findViewById(R.id.MainWebview);
		mWebView.getSettings().setJavaScriptEnabled(true);

		// 웹뷰에 줌 달기
		mWebView.getSettings().setBuiltInZoomControls(true);

		// 웹뷰에 딱맞게 페이지 크기조절
		final WebSettings webSetting = mWebView.getSettings();
		webSetting.setLoadWithOverviewMode(true);
		webSetting.setUseWideViewPort(true);

		mWebView.loadUrl("http://www.naver.com");
		mWebView.setWebViewClient(new HelloWebViewClient());


posted by 젊은쎄오
: