=======================
=======================
=======================
출처: http://hyunssssss.tistory.com/297
뷰페이저(ViewPager)
- 화면을 좌우로 드래그해서 화면을 전환하는 기법이다.
- import android.support.v4.app.~~ 를 선언해야한다.
●예제
- 버튼을 클릭하거나 좌우로 드래그하면 화면이 전환되고 2초마다 화면이 자동으로 전환되는 예제
- MainActivity.java
package com.example.day0404; import android.os.Bundle; import android.os.Handler; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBarActivity; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class MainActivity extends ActionBarActivity implements OnClickListener{ Button btn[] = new Button[3]; ViewPager viewPager = null; Thread thread = null; Handler handler = null; int p=0; //페이지번호 int v=1; //화면 전환 뱡향 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //viewPager viewPager = (ViewPager)findViewById(R.id.viewPager); MyViewPagerAdapter adapter = new MyViewPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(adapter); btn[0] = (Button)findViewById(R.id.btn_a); btn[1] = (Button)findViewById(R.id.btn_b); btn[2] = (Button)findViewById(R.id.btn_c); for(int i=0;i<btn.length; i++){ btn[i].setOnClickListener(this); } handler = new Handler(){ public void handleMessage(android.os.Message msg) { if(p==0){ viewPager.setCurrentItem(1); p++; v=1; }if(p==1&&v==0){ viewPager.setCurrentItem(1); p--; }if(p==1&&v==1){ viewPager.setCurrentItem(2); p++; }if(p==2){ viewPager.setCurrentItem(1); p--; v=0; } } }; thread = new Thread(){ //run은 jvm이 쓰레드를 채택하면, 해당 쓰레드의 run메서드를 수행한다. public void run() { super.run(); while(true){ try { Thread.sleep(2000); handler.sendEmptyMessage(0); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; thread.start(); } @Override public void onClick(View v) { switch(v.getId()){ case R.id.btn_a: viewPager.setCurrentItem(0); Toast.makeText(this,"A버튼", Toast.LENGTH_SHORT).show(); break; case R.id.btn_b: viewPager.setCurrentItem(1); Toast.makeText(this,"B버튼", Toast.LENGTH_SHORT).show(); break; case R.id.btn_c: viewPager.setCurrentItem(2); Toast.makeText(this,"C버튼", Toast.LENGTH_SHORT).show(); break; default: break; } } } |
- activity_main.xml
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="${relativePackage}.${activityClass}" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/btn_a" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="A" style="@style/Widget.AppCompat.ActionButton"/> <Button android:id="@+id/btn_b" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="B" style="@style/Widget.AppCompat.ActionButton"/> <Button android:id="@+id/btn_c" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="C" style="@style/Widget.AppCompat.ActionButton"/> </LinearLayout> <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout> |
- MyViewPagerAdapter.java
package com.example.day0404; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; public class MyViewPagerAdapter extends FragmentStatePagerAdapter{ /* * 이 클래스의 부모생성자 호출시 인수로 반드시 FragmentManager객체를 넘겨야한다. * 이 객체는 Activity에서만 만들수 있고, 여기서사용중인 Fragment가 v4이기 때문에 * Activity중에서도 ActionBarActivity에서 얻어와야한다. */ Fragment[] fragments = new Fragment[3]; public MyViewPagerAdapter(FragmentManager fm) { super(fm); fragments[0] = new FragmentA(); fragments[1] = new FragmentB(); fragments[2] = new FragmentC(); } //아래의 메서드들의 호출 주체는 ViewPager이다. //ListView와 원리가 같다. /* * 여러 프레그먼트 중 어떤 프레그먼트를 보여줄지 결정 */ public Fragment getItem(int arg0) { return fragments[arg0]; } /* * 보여질 프레그먼트가 몇개인지 결정 */ public int getCount() { return fragments.length; } } |
- FragmentA.java
package com.example.day0404; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class FragmentA extends Fragment{ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_a, container, false); } } |
- fragment_a.xml
<?xml version="1.0" encoding="utf-8"?> android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#3DB7CC"> </LinearLayout> |
- FragmentB.java, fragment_b.xml, FragmentC.java, fragment_c.xml 동일
=======================
=======================
=======================
출처: http://kitesoft.tistory.com/76
이전 포스트에서 소개한
ViewFlipper는
대량의 이미지나 텍스트를
하나씩 변경하면서 소개하는
'슬라이드 보기' 같은
기능에 적합합니다.
이번에 소개할 예제소스는
'만화보기'처럼
Page를 넘겨가면서
보는데 적합한 예제입니다.
마치 Page를 넘기듯이
손가락으로 드래그해서
좌우로 넘기는 UI를 보신적이 있을 겁니다.
안드로이드의 기본 api에는 제공되지 않지만
Support Library에 포함되어 있는 녀석입니다.
ViewPager 라는 이름을 가지고 있으며
이름으로도 연상이 되듯이 View들을 Page를 넘기듯
보여주는 AdapterView의 일종입니다.
AdapterView의 대표적인 종류인
ListView와 매우 비슷하게 제작됩니다.
ListView의 CustomAdapter를 구현할 수 있다면
어렵지 않게 이해하실 수 있습니다.
혹시 잘 모르시는 분은
[안드로이드 Android] 리스트 뷰(ListView) 6. Custom ListView(사용자 정의 리스트 뷰) - BaseAdapter
포스트를 참고하세요.
이번에 소개할 ViewPager예제는
많은 양의 이미지를 한장씩 Page를 넘기면서
볼 수있는 예제입니다.
이미지는 10장만 쓰겠습니다.
손가락으로 넘길 수도 있으며
위의 버튼을 이용해서 앞뒤장을 넘어갈 수도 있게 하겠습니다.
먼저 결과화면을 그림 여러장으로 보여드리겠습니다.
시작화면-첫번째 페이지
두번째 Page로 드래그해서 넘기기
세번째 Page로 드래그해서 넘기기
Next버튼으로 넘기기
Previous 버튼으로 앞장으로 넘기기
사진으로도 충분히 이해가 가능하리라 판단되어
gif 파일로 보여드리지는 않습니다.
자. 이제 결과화면을 봤으니
만들어 보겠습니다.
먼저 그림에 사용할 이미지 10장은
첨부파일에서 다운받으시기 바랍니다.
가장 먼저 Main 레이아웃 파일입니다.
ListView와 매우 흡사합니다.
가장 주의할 것은 ViewPager를 기본 api가 아니므로
android.support.v4.view 패키지명을 앞에 표기해야 한다는 겁니다.
자동으로 작성되지 않으므로 철자에 주의해서 작성 하셔야 합니다.
activity_main.xml |
<LinearLayout 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" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="3dp"> <Button android:id="@+id/btn_previous" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="PREVIOUS" android:textSize="12sp" android:onClick="mOnClick"/> <Button android:id="@+id/btn_next" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Next" android:textSize="12sp" android:onClick="mOnClick"/> </LinearLayout> <android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v4.view.ViewPager> </LinearLayout> |
굵은 글씨의 ViewPager는 AdapterView 입니다.
Adapter란,
대량의 Data(여기서는 Image들)들을
보여주기 위해 View를 여러개 만들어서 ViewGroup에 추가해줘야 하는데
이 작업을 Data의 순서에 맞게 적절한 View로 만들어 주는 클래스 객체를 말합니다.
(Custom ListView 참조)
이 ViewPager에 붙일 Adapter는
위 설명처럼 Image를 보여주기 위한 ImageView를 만들어 내는 기능이 있어야 합니다.
그래서 우선 만들어낼 ImageView의 모양을 설정하는 Layout 파일부터 만들겠습니다.
viewpager_childview.xml |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ImageView android:id="@+id/img_viewpager_childimage" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout> |
ViewPager가 만들어낼 한 Page의 모양이 이렇게 생길 것이다 라는 것이죠.
이 예제에서는 ImageView 하나를 화면에 꽉 차게 만들겠다는 거죠.
이제 이 레이아웃 파일을 실제
View 객체로 만들어서 해당하는 순번에 따라 적절한 Image를 보여주는
Adapter를 만들어 보겠습니다.
이전에 소개한 Custom ListView의 예제와 같은 작업인데
그때는 BaseAdapter를 상속받아 만들었습니다.
이번 Adapter는 PagerAdapter를 상속받아 만든다는 것이 다르며
안에 오버라이드해서 만들 메소드도 약간 차이가 있습니다.
세부 설명은 주석으로 표기했습니다.
지면상 간략한 설명이어서 이해하기 어려울 수도 있으니
코드를 보고 해석할 수 있는 부분까지 해석해서
활용하시고 좀더 깊은 이해는 다른 자료들을 서베이 하시기 바랍니다.
PagerAdapter를 상속받은 CustomAdapter 클래스를 설계합니다.
만드는 과정은 그림으로 보여드립니다.
이렇게 만드시면 자동으로 2개의 메소드가 오버라이드(override) 되어 있을 겁니다.
getCount(), isViewFromObject()
추가적으로 필요한 2개의 메소드를 오버라이드 합니다.
instantiateItem(), destroyItem()
파라미터 중 첫번째가 ViewGroup인것을 오버라이드합니다.
View인 것은 deprecated 되었습니다.
커스텀 아답터(Custom Adapter) 소스 파일입니다.
CustomAdapter.java |
public class CustomAdapter extends PagerAdapter { LayoutInflater inflater; public CustomAdapter(LayoutInflater inflater) { // TODO Auto-generated constructor stub //전달 받은 LayoutInflater를 멤버변수로 전달 this.inflater=inflater; } //PagerAdapter가 가지고 잇는 View의 개수를 리턴 //보통 보여줘야하는 이미지 배열 데이터의 길이를 리턴 @Override public int getCount() { // TODO Auto-generated method stub return 10; //이미지 개수 리턴(그림이 10개라서 10을 리턴) } //ViewPager가 현재 보여질 Item(View객체)를 생성할 필요가 있는 때 자동으로 호출 //쉽게 말해, 스크롤을 통해 현재 보여져야 하는 View를 만들어냄. //첫번째 파라미터 : ViewPager //두번째 파라미터 : ViewPager가 보여줄 View의 위치(가장 처음부터 0,1,2,3...) @Override public Object instantiateItem(ViewGroup container, int position) { // TODO Auto-generated method stub View view=null; //새로운 View 객체를 Layoutinflater를 이용해서 생성 //만들어질 View의 설계는 res폴더>>layout폴더>>viewpater_childview.xml 레이아웃 파일 사용 view= inflater.inflate(R.layout.viewpager_childview, null); //만들어진 View안에 있는 ImageView 객체 참조 //위에서 inflated 되어 만들어진 view로부터 findViewById()를 해야 하는 것에 주의. ImageView img= (ImageView)view.findViewById(R.id.img_viewpager_childimage); //ImageView에 현재 position 번째에 해당하는 이미지를 보여주기 위한 작업 //현재 position에 해당하는 이미지를 setting img.setImageResource(R.drawable.gametitle_01+position); //ViewPager에 만들어 낸 View 추가 container.addView(view); //Image가 세팅된 View를 리턴 return view; } //화면에 보이지 않은 View는파쾨를 해서 메모리를 관리함. //첫번째 파라미터 : ViewPager //두번째 파라미터 : 파괴될 View의 인덱스(가장 처음부터 0,1,2,3...) //세번째 파라미터 : 파괴될 객체(더 이상 보이지 않은 View 객체) @Override public void destroyItem(ViewGroup container, int position, Object object) { // TODO Auto-generated method stub //ViewPager에서 보이지 않는 View는 제거 //세번째 파라미터가 View 객체 이지만 데이터 타입이 Object여서 형변환 실시 container.removeView((View)object); } //instantiateItem() 메소드에서 리턴된 Ojbect가 View가 맞는지 확인하는 메소드 @Override public boolean isViewFromObject(View v, Object obj) { // TODO Auto-generated method stub return v==obj; } } |
ListView의 CustomAdapter와 방법은 다소 다르지만
해야하는 작업은 같습니다.
이제 이 CustomAdapter를 ViewPager에 적용하고
버튼으로 제어하는 작업을 하겠습니다.
메인 액티비티 소스파일입니다.
설명은 주석을 참고하세요.
MainActivity.java |
public class MainActivity extends Activity { ViewPager pager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pager= (ViewPager)findViewById(R.id.pager); //ViewPager에 설정할 Adapter 객체 생성 //ListView에서 사용하는 Adapter와 같은 역할. //다만. ViewPager로 스크롤 될 수 있도록 되어 있다는 것이 다름 //PagerAdapter를 상속받은 CustomAdapter 객체 생성 //CustomAdapter에게 LayoutInflater 객체 전달 CustomAdapter adapter= new CustomAdapter(getLayoutInflater()); //ViewPager에 Adapter 설정 pager.setAdapter(adapter); } //onClick속성이 지정된 View를 클릭했을때 자동으로 호출되는 메소드 public void mOnClick(View v){ int position; switch( v.getId() ){ case R.id.btn_previous://이전버튼 클릭 position=pager.getCurrentItem();//현재 보여지는 아이템의 위치를 리턴 //현재 위치(position)에서 -1 을 해서 이전 position으로 변경 //이전 Item으로 현재의 아이템 변경 설정(가장 처음이면 더이상 이동하지 않음) //첫번째 파라미터: 설정할 현재 위치 //두번째 파라미터: 변경할 때 부드럽게 이동하는가? false면 팍팍 바뀜 pager.setCurrentItem(position-1,true); break; case R.id.btn_next://다음버튼 클릭 position=pager.getCurrentItem();//현재 보여지는 아이템의 위치를 리턴 //현재 위치(position)에서 +1 을 해서 다음 position으로 변경 //다음 Item으로 현재의 아이템 변경 설정(가장 마지막이면 더이상 이동하지 않음) //첫번째 파라미터: 설정할 현재 위치 //두번째 파라미터: 변경할 때 부드럽게 이동하는가? false면 팍팍 바뀜 pager.setCurrentItem(position+1,true); break; } } } |
코드는 엄처 간단하죠.
실제로 매우 사용성이 높은 View이니 만큼 여러분이
제작하고자 하는 앱에 적절히 활용할 수 있기를 바랍니다.
=======================
=======================
=======================
출처: http://kitesoft.tistory.com/75
여러장의 이미지나 텍스트등을
한장씩 넘겨가면서 보고싶을 때
유용한 ViewFilpper 예제입니다.
View Animator의 일종으로서
FrameLayout을 상속받아 만들어졌습니다.
컴퓨터에서
여러장의 이미지를 하나씩 볼때 사용하는
뷰어를 만든다고 보시면 됩니다.
다음 장으로 넘어가는 'Next' 버튼,
이전 장으로 넘어가는 'Previous' 버튼,
자동으로 일정 간격으로 View가 변경되는 'Auto' 버튼이 있습니다.
'슬라이드 보기' 같은 기능이죠.
참고로 이 'Auto'버튼은 on, off가 되도록 ToggleButton을
사용했습니다.
우선 결과 화면입니다.
먼저 버튼을 눌러서 이미지를 변경하는 모습입니다.
자동으로 이미지가 변경되는 모습입니다.
동작에 대한 이해는 어렵지 않겠죠.
이제 만들어 보겠습니다.
사용된 이미지는 첨부파일을 다운받으시거나
다른 이미지를 사용하셔도 좋습니다.
총 10장의 이미지인데
xml 레이아웃파일에 10개의 ImageView를 만들자니
너무 길어서..
우선 3개반 xml에 만들고
나머지 7개는 Java에서 반복문을 이용해서
ViewFlipper에 추가(addView) 했습니다.
레이아웃 파일입니다.
activity_main.xml |
<LinearLayout 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" android:orientation="vertical" android:padding="5dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:weightSum="3"> <Button android:id="@+id/btn_previous" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="PREVIOUS" android:textSize="14sp" android:onClick="mOnClick"/> <ToggleButton android:id="@+id/toggle_auto" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textOff="AUTO" android:textOn="STOP" android:textSize="14sp"/> <Button android:id="@+id/btn_next" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="NEXT" android:textSize="14sp" android:onClick="mOnClick"/> </LinearLayout> <ViewFlipper android:id="@+id/flipper" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/img01" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/gametitle_01"/> <ImageView android:id="@+id/img02" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/gametitle_02"/> <ImageView android:id="@+id/img03" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/gametitle_03"/> </ViewFlipper> </LinearLayout> |
ViewFipper 안에 3개의 ImageView가 보이시죠?
이제 ViewFlipper를 제어하는 자바 소스파일입니다.
설명은 주석을 참고하세요.
MainActivity.java |
public class MainActivity extends Activity { ViewFlipper flipper; //자동 Flipping 선택 ToggleButton 참조변수 ToggleButton toggle_Flipping; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //ViewFlipper 객체 참조 flipper= (ViewFlipper)findViewById(R.id.flipper); //총 10개의 이미지 중 3개만 XML에서 ImageView로 만들었었음. //소스코드에서 ViewFipper에게 나머지 7개의 이미지를 추가하기위해 //ImageView 7개를 만들어서 ViewFlipper에게 추가함 //layout_width와 layout_height에 대한 특별한 지정이 없다면 //기본적으로 'match_parent'로 설정됨. for(int i=0;i<7;i++){ ImageView img= new ImageView(this); img.setImageResource(R.drawable.gametitle_04+i); flipper.addView(img); } //ViewFlipper가 View를 교체할 때 애니메이션이 적용되도록 설정 //애니메이션은 안드로이드 시스템이 보유하고 있는 animation 리소스 파일 사용. //ViewFlipper의 View가 교체될 때 새로 보여지는 View의 등장 애니메이션 //AnimationUtils 클래스 : 트윈(Tween) Animation 리소스 파일을 Animation 객체로 만들어 주는 클래스 //AnimationUtils.loadAnimaion() - 트윈(Tween) Animation 리소스 파일을 Animation 객체로 만들어 주는 메소드 //첫번째 파라미터 : Context //두번재 파라미터 : 트윈(Tween) Animation 리소스 파일(여기서는 안드로이드 시스템의 리소스 파일을 사용 // (왼쪽에서 슬라이딩되며 등장) Animation showIn= AnimationUtils.loadAnimation(this, android.R.anim.slide_in_left); //ViewFlipper에게 등장 애니메이션 적용 flipper.setInAnimation(showIn); //ViewFlipper의 View가 교체될 때 퇴장하는 View의 애니메이션 //오른쪽으로 슬라이딩 되면 퇴장하는 애니메이션 리소스 파일 적용. //위와 다른 방법으로 애니메이션을 적용해봅니다. //첫번째 파라미터 : Context //두번재 파라미터 : 트윈(Tween) Animation 리소스 파일(오른쪽으로 슬라이딩되며 퇴장) flipper.setOutAnimation(this, android.R.anim.slide_out_right); //자동 Flipping 선택 ToggleButton에 따른 작업 toggle_Flipping= (ToggleButton)findViewById(R.id.toggle_auto); //ToggleButton에 Toggle상태 변경 리스너 세팅(OnCheckedChangedListener) toggle_Flipping.setOnCheckedChangeListener(new OnCheckedChangeListener() { //ToggleButton의 선택 상태가 변경되면 자동으로 호출되는 메소드 //첫번재 파라미터 : 선택의 변화가 생긴 CompoundButton(여기서는 toggle_Flipping) //두번째 파라미터 : Compoundbutton(toggle_Flipping)의 ON(true),OFF(false) 상태 @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // TODO Auto-generated method stub if(isChecked){//On 으로 바뀌었으므로 ..자동 Flipping 시작.. //1초의 간격으로 ViewFlipper의 View 자동 교체 flipper.setFlipInterval(1000);//플리핑 간격(1000ms) flipper.startFlipping();//자동 Flipping 시작 }else{//OFF로 변경되었으므로 Flipping 정지 flipper.stopFlipping();;//Flipping 정지 } } }); } //onClick속성이 지정된 View가 클릭되었을 때 자동으로 호출되는 메소드. public void mOnClick(View v){ switch( v.getId() ){ case R.id.btn_previous: flipper.showPrevious();//이전 View로 교체 break; case R.id.btn_next: flipper.showNext();//다음 View로 교체 break; } } } |
여기까지 ViewFiipper 입니다.
여러 이미지의 교체를 매우 손쉽게 합니다.
그런데. 잠시
이렇게 여러 이미지를 교체하는 앱 중에서
만화 보기 처럼 손으로 드래그 해서
이미지를 좌우로 바꾸는 어플들을 보셨을 겁니다.
이런 경우는 ViewFipper 보다
만화책의 Page를 넘기는 듯한 모습이 되는
ViewPager 라는 것이 있습니다.
사용성이 좋은 View 중에 하나죠.
다음 포스트에서 ViewPager를 만들어보겠습니다.
=======================
=======================
=======================
▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ViewPager란?? ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒
ViewPager란 수평으로 View를 좌/우 로 스크롤 할때 사용 할때 사용하는 클래스 입니다. 안드로이드 기본으로 지원하는 클래스는 아니지만 안드로이드 제공하는 'Compatibility Package Revison 3' Support 라이브러리에 포함 되어 안드로이드 1.6 이후 버젼 이후에는 사용이 가능 합니다.
일반적으로 좌/우 로 화면을 전환할 경우 Gallery, HorizontalScrollView, ViewFlipper, ViewSwitcher등을 이용하여 개발을 많이 하는데요 전문적으로 화면전환을 위해 만들어진 Viewpager를 이용하면 좀 더 편리하고 기능적인 화면을 구성 할 수 있습니다.
아래는 Viewpager의 동작 방식 입니다.
(구글 마켓이나 구글+ 도 Viewpager방식으로 만들어 졌습니다.)
Viewpager는 위의 그림 처럼 좌/우 로 이동 시켜 주면 화면이 전환 되면서 동작 합니다. |
▒ ▒ ▒ ▒ ▒ ▒ ▒ ViewPager 를 위한 설치 준비 ▒ ▒ ▒ ▒ ▒ ▒ ▒
기본적으로 Viewpager를 사용하기 위해서는 Support Libary를 사용 해야 합니다. 안드로이드 SDK에서 기본으로 제공해주는 클래스가 아니기 때문에"android-support-v4.jar"라는 Libary를 추가 해서 사용 해야 합니다.
"android-support-v4.jar" 를 사용하기 위해서는 Viewpager를 사용할 Project의 Properties에서 Java Build Path(Alt + Enter)를 선택 합니다. Java Build Path의 Libraries탭을 선택하고 외부에서 직접 Jar파일을 추가 할 수 있는 Add External JARs... 를 선택 합니다. 그러면 두번째 그림과 같이 JAR Selection 창이 나오는데 아래와 같이 추가해 주시면 됩니다.
Add External JARs... 를 선택후, 자신의 Android 설치 폴더의 extras -> android -> support -> v4 폴더에 가면 "android-support-v4.jar" 라이브러리를 추가 할 수 있습니다. |
< 만약에 폴더가 없거나 라이브러리를 추가 할 수 없다면, 최신 버젼의 SDK와 ADT 를 설치 해 보시고 다시 추가해 보시기 바랍니다. >
▒ ▒ ▒ ▒ ▒ ▒ ▒ XML에 ViewPager 적용 하기 ▒ ▒ ▒ ▒ ▒ ▒ ▒
라이브러리를 재대로 추가 한 후에 XML에 ViewPager를 사용한 코드 입니다. Viewpager를 사용하기 위해서는<android.support.v4.view.ViewPager> 클래스를 풀네임을 모두 적어 주어야 하는데요. 그 이유는 아까 말씀 드렸듯이 안드로이드가 기본적으로 지원하는 클래스가 아니기 때문 입니다.
<android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@+id/bottom" android:layout_below="@+id/ll_title_layout" > </android.support.v4.view.ViewPager> |
▒ ▒ ▒ ▒ ▒ ▒ ▒ Java 전체 코드 입니다. ▒ ▒ ▒ ▒ ▒ ▒ ▒
package arabiannight.tistory.com; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class TestViewPagerActivity extends Activity implements OnClickListener{ private ViewPager mPager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); setLayout(); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(new PagerAdapterClass(getApplicationContext())); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_one: setCurrentInflateItem(0); break; case R.id.btn_two: setCurrentInflateItem(1); break; case R.id.btn_three: setCurrentInflateItem(2); break; } } private void setCurrentInflateItem(int type){ if(type==0){ mPager.setCurrentItem(0); }else if(type==1){ mPager.setCurrentItem(1); }else{ mPager.setCurrentItem(2); } } private Button btn_one; private Button btn_two; private Button btn_three; /** * Layout */ private void setLayout(){ btn_one = (Button) findViewById(R.id.btn_one); btn_two = (Button) findViewById(R.id.btn_two); btn_three = (Button) findViewById(R.id.btn_three); btn_one.setOnClickListener(this); btn_two.setOnClickListener(this); btn_three.setOnClickListener(this); } private View.OnClickListener mPagerListener = new View.OnClickListener() { @Override public void onClick(View v) { String text = ((Button)v).getText().toString(); Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show(); } }; /** * PagerAdapter */ private class PagerAdapterClass extends PagerAdapter{ private LayoutInflater mInflater; public PagerAdapterClass(Context c){ super(); mInflater = LayoutInflater.from(c); } @Override public int getCount() { return 3; } @Override public Object instantiateItem(View pager, int position) { View v = null; if(position==0){ v = mInflater.inflate(R.layout.inflate_one, null); v.findViewById(R.id.iv_one); v.findViewById(R.id.btn_click).setOnClickListener(mPagerListener); } else if(position==1){ v = mInflater.inflate(R.layout.inflate_two, null); v.findViewById(R.id.iv_two); v.findViewById(R.id.btn_click_2).setOnClickListener(mPagerListener); }else{ v = mInflater.inflate(R.layout.inflate_three, null); v.findViewById(R.id.iv_three); v.findViewById(R.id.btn_click_3).setOnClickListener(mPagerListener); } ((ViewPager)pager).addView(v, 0); return v; } @Override public void destroyItem(View pager, int position, Object view) { ((ViewPager)pager).removeView((View)view); } @Override public boolean isViewFromObject(View pager, Object obj) { return pager == obj; } @Override public void restoreState(Parcelable arg0, ClassLoader arg1) {} @Override public Parcelable saveState() { return null; } @Override public void startUpdate(View arg0) {} @Override public void finishUpdate(View arg0) {} } } |
▒ ▒ ▒ ▒ ▒ ▒ ▒ 코드 설명 입니다. ▒ ▒ ▒ ▒ ▒ ▒ ▒
ViewPager 표시하는 View는 PagerAdapter를 통해 공급 받습니다. PagerAdapter를 통해 화면에 표시 될 View의 라이프 사이클을 관리 할 수 있습니다. 일반적인 ListView와 ListAdapter의 관계와 동일하다고 이해 하시면 됩니다. 즉, Adapter를 통해 ViewPager에 표시되는 Data들을 관리한다는 내용 입니다.
VieaPager와 PagerAdapter를 연결 시켜 주는 부분 입니다. setAdapter를 통해 연결 합니다.
mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(new PagerAdapterClass(getApplicationContext())); |
PagerAdapter 상속 구현 클래스 부분 입니다. PagerAdapter에서 instantiateItem() 라는 오버라이드 메서드가 있는데 ViewPager의 getCount()에서 얻어온 Count의 Position별로 Pager에 등록할 item을 처리 할 수 있는 메서드 입니다. 즉, VieaPager에서 사용할 뷰객체를 생성하는 작업 입니다.
단 주의 해야 할 점이 있는데,
처음 실행시 PagerAdapter는 현재의 position의 Item의 양쪽 옆의 View들을 모두 instantiateItem() 해준다는 점인데요. 처음 시작시에는 position션이 첫번째인 item을 보여주기 때문에 아래와 같은 형태로 View를 그려 줍니다.
처음 저희가 사용할 View는 총 3개의 View입니다.(Position 첫번째, 두번째 , 세번째)
1. 첫번째 포지션으로 시작했을 경우 입니다. ( 첫번째 두번째 View가 모두 생성 됩니다. 이말은 즉 instantiateItem() 을 두번 탓다는 얘기 입니다.)
2. 두번째 포지션으로 이동했을 경우 입니다. ( 세번째 포지션이 생성 됩니다. 그리고 첫번째 포지션도 유지하고 있습니다. ) 포지션이동은 좌/우 스크롤을 이용하거나 setCurrentInflateItem(int position) 메서드로 원하는 포지션으로 이동할 수 있습니다.
2. 세번째 포지션으로 이동했을 경우 입니다. ( 첫번째 포지션을 삭제 합니다. )
이러한 형태로 PagerAdapter의 View관리가 이루어 집니다. 항상 양쪽의 View를 생성하거나 유지 시키고 나머지 포지션은 삭제 하는 형태 입니다. |
▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ PagerAdapter API 설명 ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒
getCount() : 현재 PagerAdapter에서 관리할 갯수를 반환 한다. instantiateItem() : ViewPager에서 사용할 뷰객체 생성 및 등록 한다. destroyItem() : View 객체를 삭제 한다. isViewFromObject() : instantiateItem메소드에서 생성한 객체를 이용할 것인지 여부를 반환 한다. restoreState() : saveState() 상태에서 저장했던 Adapter와 page를 복구 한다. saveState() : 현재 UI 상태를 저장하기 위해 Adapter와 Page 관련 인스턴스 상태를 저장 합니다. startUpdate() : 페이지 변경이 시작될때 호출 됩니다. finishUpdate() : 페이지 변경이 완료되었을때 호출 됩니다. |
▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ 첨부파일 ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒
▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ 참고 사이트 ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒
출처 : http://blog.daum.net/mailss/16
출처 : http://blog.naver.com/PostView.nhn?blogId=huewu&logNo=110116958816
=======================
=======================
=======================
출처: http://pulsebeat.tistory.com/27
[안드로이드] 터치화면, 제스처 기능을 이용한 터치 인식
오늘은 터치화면을 이용한 제스처 기능에 대해서 알아보겠습니다. 제스처 기능은 말그래로 "동작, 움직임" 을 감지합니다. 하지만 핸드폰 자체의 센서를 이용한 움직임은 아닙니다. 즉, 핸드폰을 흔들거나, 기울이거나를 해서 얻는 이벤트를 이용해서 어떠한 기능을 수행하는 기능이 아닙니다. 제스처 기능은 화면 내의 터치를 감지하여, 어떠한 이벤트가 들어왔는지를 분석하여 어떤 기능을 수행할 수 있도록 합니다. 간단한 예로는 화면을 넘기는 것을 예로 들 수 있습니다. 흔히 메인 화면은 여러 개의 화면으로 구성되어 있습니다. 왼쪽에서 오른쪽으로 화면을 넘기기 위해서 손가락을 이용해서 왼쪽에서 오른쪽으로 드래그하는 동작을 취합니다. 이런 방식으로 손가락 모션을 인식하는 것이 제스처 기능입니다.
아래의 예제 코드는 왼쪽에서 오른쪽으로 드래그했을 때, 오른쪽에서 왼쪽으로 드래그를 했을 때, 아래에서 위로, 위에서 아래로 손가락으로 드래그했을 시에 이벤트를 발생시킬 수 있도록 하는 기능을 담고 있습니다. 이벤트가 발생하면 간단하게 토스트를 띄워서 제대로 동작하고 있는지를 확인하도록 되어있습니다. 그리고 부가적으로, 한번 클릭했을 때, 길게 클릭했을 때 등의 기능도 포함되어 있습니다.
Android & Amir 홈페이지에서 가져왔으며, 한 두가지 오류를 찾아 수정한 것입니다.
[ 제스처 결과 화면 ]
(오른쪽 방향으로 Swipe하였을 때)
STEP 1 Java Source Code
예제는 간단한 한 화면만 있습니다. 상위에는 현재 마우스 클릭(손가락 터치)이 어떠한 방식으로 되고 있는지 출력하는 텍스트 박스가 있으며, 하위에는 마우스 제스처(손가락 터치 모션) 를 테스트해볼 수 있는 화면이 구성되어 있습니다. 레이아웃은 Xml을 통해 구현하지 않았고, 바로 코드를 이용해서 추가하도록 하였습니다. 눈여겨 봐야할 것은, 제스처를 인식하는 방법입니다.
예제 코드 Touch UI Gesture (제스처 기능)
import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.view.GestureDetector; import android.view.Gravity; import android.view.MotionEvent; import android.view.GestureDetector.OnGestureListener; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; public class GestureActivity extends Activity implements OnGestureListener { private LinearLayout main; private TextView viewA; private static final int SWIPE_MIN_DISTANCE = 120; private static final int SWIPE_MAX_OFF_PATH = 250; private static final int SWIPE_THRESHOLD_VELOCITY = 200; private GestureDetector gestureScanner; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); gestureScanner = new GestureDetector(this); main = new LinearLayout(this); main.setBackgroundColor(Color.GRAY); main.setLayoutParams(new LinearLayout.LayoutParams(320, 480)); viewA = new TextView(this); viewA.setBackgroundColor(Color.WHITE); viewA.setTextColor(Color.BLACK); viewA.setTextSize(30); viewA.setGravity(Gravity.CENTER); viewA.setLayoutParams(new LinearLayout.LayoutParams(320, 80)); main.addView(viewA); setContentView(main); } @Override public boolean onTouchEvent(MotionEvent me) { return gestureScanner.onTouchEvent(me); } public boolean onDown(MotionEvent e) { viewA.setText("-" + "DOWN" + "-"); return true; } public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { try { if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) return false; // right to left swipe if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { Toast.makeText(getApplicationContext(), "Left Swipe", Toast.LENGTH_SHORT).show(); } // left to right swipe else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { Toast.makeText(getApplicationContext(), "Right Swipe", Toast.LENGTH_SHORT).show(); } // down to up swipe else if (e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) { Toast.makeText(getApplicationContext(), "Swipe up", Toast.LENGTH_SHORT).show(); } // up to down swipe else if (e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) { Toast.makeText(getApplicationContext(), "Swipe down", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { } return true; } public void onLongPress(MotionEvent e) { Toast mToast = Toast.makeText(getApplicationContext(), "Long Press", Toast.LENGTH_SHORT); mToast.show(); } public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { viewA.setText("-" + "SCROLL" + "-"); return true; } public void onShowPress(MotionEvent e) { viewA.setText("-" + "SHOW PRESS" + "-"); } public boolean onSingleTapUp(MotionEvent e) { Toast mToast = Toast.makeText(getApplicationContext(), "Single Tap", Toast.LENGTH_SHORT); mToast.show(); return true; } } |
Swipe(휘두르다, 강타)라는 용어를 썼는데, 한국어로 어떻게 변경을 해야할지 몰라 그대로 두었습니다. 손가락을 이용하여 여러 개로 구성되어 있는 메인 화면을 넘기는 방법을 생각하시면 가장 간단할 것 같습니다.
코드의 핵심은 GestureDetector 에 있습니다. 엑티비티에서 OnGestureListener 를 상속받으면, GestureDetector를 사용할 수 있습니다. GestureDetector를 엑티비티에 등록시켜면서 생성합니다. 그런 다음 onTouchEvent를 통해 GestureDetector 객체의 onTouchEvent로 등록시켜줍니다. 이렇게 하면 기본 세팅은 마무리가 됩니다. 그런 다음에 자신이 하고 싶은 기능을 상속받아 사용할 수 있습니다. 위에서 보시면, onDown, onLongPress, onScroll, onShowPress, onSingleTapUp, onFling라는 것을 상속받아 구현되어 있는 것을 확인할 수 있습니다. 함수명에서 보시듯 어떠한 터치 동작을 하고 있는 중인지에 따라 각각이 호출되고 있습니다. 다른 것은 직관적으로 아실 수 있을 거라 생각이 듭니다만, 복잡한 듯 보이는 onFling은 조금 생각을 해보아야 합니다. 아래의 세가지 변수를 먼저 알아봅시다. 이것만 이해되신다면 아주 간단한 구조로 되어 있다는 것을 알게 되실 겁니다.
private static final int SWIPE_MIN_DISTANCE = 120; private static final int SWIPE_MAX_OFF_PATH = 250; private static final int SWIPE_THRESHOLD_VELOCITY = 200; |
터치 이벤트를 통해서 onFling이벤트로 들어옵니다. onFling으로 들어오는 인자를 보시면, 모션 이벤트가 2개가 들어오고, float 형의 velocity(속도)가 들어옵니다. 벌써 이미 느낌이 오실 겁니다. 첫 번째 인자는 터치를 시작한 그 순간의 이벤트이며, 두 번째 인자는 터치를 땐 그 시점의 이벤트 입니다. 세 번째 인자는 X축으로의 속도이며, 네 번째 인자는 Y 축으로의 인자입니다. 즉 이 네 가지의 이벤트를 가지고, 어떤 방향으로의 Swipe동작인지 구분할 수가 있습니다.
네 가지 방향으로의 Swipe동작을 어떻게 구분하는가 하는 것은 거리 측정과, 속도를 통해서 이루어집니다. SWIPE_MIN_DISTANSE 인자는 Swipe를 인식하는 가장 작은 거리를 뜻합니다. 설정된 거리 이상이 되어야 Swipe동작으로 인식한다는 것이죠. SWIPE_MAX_OFF_PATH는 인식하는 최대 거리라고 보시면 됩니다. 그 이상의 거리를 Scrolling하면 제외하겠다는 의미를 가지고 있습니다. 마지막 SWIPE_THRESHOLD_VELOCITY는 속도입니다. 각 방향으로 임의의 속도 이상으로 터치동작이 이루어져야 Swipe로 인식하겠다는 것이지요.
이제 위의 코드를 살펴보시면, 어떤 구조로 되어 있는지 한 눈에 들어오실 겁니다. 첫 좌표와 끝 좌표를 비교하여 거리를 비교하여 적정 수준의 거리와 속도를 가지면 Swipe로 인식하며 토스트를 띄웁니다. 이러한 방식을 이용하여 화면 전환 등의 자신이 하고 싶은 동작을 넣으시면 됩니다.
STEP 2 Xml Code
Xml 코드를 제외하고 코드로 바로 레이아웃을 설정하였습니다. onCreate에 있는 추가동작을 xml로 구현하셔도 무방합니다.
STEP 3 AndroidManifest.xml Code
안드로이드 자체에서 제공되는 서비스기 때문에 메니페스트는 손댈 필요가 없습니다.
<마무리> 터치화면, 제스처 기능을 이용한 터치 인식
일반적으로 다이나믹한 UI를 사용자에게 제공하기 위해서는 터치 기능을 이용하여 다양한 서비스를 제공해야합니다. 제스처(Gesture)를 인식하기 위해서는 이벤트 핸들러를 통해서 현재 어떠한 모션을 취하고 있는지를 확인하는 절차가 필요합니다. onGestureListener를 Activity로 상속받고, GestureDetector를 생성하여, 터치이벤트로 등록시키며, 자신이 원하는 동작을 가져올 수 있도록, 터치 이벤트의 동작 지점, 거리, 터치 속도 등을 고려하여 설계를 하시면 됩니다. 간단한 Swipe기능에 대한 예제를 알아보고, 제스처가 어떠한 방식으로 등록되고 사용되는지에 대해서 알아보았습니다. 이것 외에도 안드로이드에서 제공하는 기본 제스처, GestureLibrary 등이 있는 것으로 생각됩니다만, 아직는 그것을 쓸 필요가 없었던 관계로 추후에 기회가 된다면 다시 포스팅 하겠습니다.
[ 참고자료 ]
=======================
=======================
=======================
출처: http://itpaper.co.kr/index.php?document_srl=2305&mid=android&listStyle=viewer&page=3
아이폰이나 안드로이드에서는 화면을 손가락을 사용하여 좌/우로 슬라이드 하였을 때 미끄러지듯이 화면이 전환되는 형태의 컴포넌트를 볼 수 있습니다.
ViewFlipper는 서로 다른 Layout을 자식으로 포함시킨 후에 앞에서 이야기한 손가락을 이용한 슬라이드 화면 전환 처리를 손쉽게 도와주는 컴포넌트 입니다.
ViewFlipper를 사용하기 위해서는 다음의 단계를 거칩니다.
1. 프로젝트 생성 후 레이아웃에 ViewFlipper 컴포넌트 배치
2. ViewFlipper안에 포함시킬 별도의 레이아웃 XML파일을 추가
3. 화면전환시에 사용될 애니메이션이 정의된 XML파일을 추가
4. 3번의 애니메이션 파일을 사용한 뷰 전환 함수 구현
5. Activity Java소스에서 ViewFlipper에 대한 객체 할당과 이벤트 정의
6. 5번 안에서 이벤트가 발생했을 때 화면 전환 구현
프로젝트의 생성과 레이아웃 작업
우선 다음과 같이 프로젝트를 생성하였습니다.
프로젝트가 생성된 후, Layout XML파일을 열어서 화면의 최 상단 컴포넌트가 LinearLayout이 되도록 한 후, 그 안에 ViewFlipper 컴포넌트를 추가시킵니다. ViewFlipper 컴포넌트는 Transitions 폴더 안에 있습니다. 추가된 ViewFlipper 컴포넌트의 id값은 R.id.viewFlipper1 입니다.
자식 화면의 XML파일 추가
ViewFlipper 안에서 전환될 화면을 정의할 Layout XML파일을 별도로 추가 합니다. 각 XML파일의 이름은 sub_view1.xml, sub_view2.xml, sub_view3.xml 로 정의하였으며, 내부에 추가적인 컴포넌트는 없으며 각 레이아웃의 배경색상을 빨강(#ff0000), 초록(#00ff00), 파랑(#0000ff)로 지정하였습니다.
화면 전환 애니메이션 파일의 추가
프로젝트의 Andorid XML File을 추가하도록 처리하고 아래의 화면을 참고하여 정보를 입력합니다. Resource Type은 Tween Animation으로 지정하고 파일의 이름은 appear_from_left.xml 입니다. Root Element는 "set"항목을 선택합니다. 같은 방식으로 appear_from_right.xml 와 disappear_to_left.xml, disappear_to_right.xml 까지 총 4개의 파일이 되도록 추가 합니다.
apprar-.xml 파일은 화면이 등장할 때의 방향과 시간을 지정하는 파일입니다. 이 파일에 정의되는 값 중에 duration이라는 값이 있는데, 이 값이 밀리세컨드 단위로 화면 전환에 걸리는 시간을 정의합니다. 여기서는 300이라고 정의하였으니 0.3초를 의미합니다. disappear-.xml 파일은 반대로 화면이 사라질 때의 방향과 시간을 정의합니다.
각 파일에 추가할 소스코드는 다음을 참고하시기 바랍니다.
appear_from_left.xml
appear_from_right.xml
disappear_to_left.xml
disappear_to_right.xml
Activity 소스의 구현
멤버변수 선언
이제 리소스의 준비가 끝났으니 직접 Acitivity 코드를 구현할 차례입니다. 우선 멤버변수 형태로 다음의 두 값을 선언합니다.
/** ViewFlipper 컴포넌트 객체 */
private ViewFlipper m_viewFlipper;
/** ViewFilpper 안에서 터치된 X축의 좌표값 */
private int m_nPreTouchPosX = 0;
컴포넌트 객체화 및 이벤트 정의
onCreate() 메서드 안에서는 선언한 ViewFlipper 객체를 할당합니다. 이 객체는 OnTouchListener라는 이벤트를 처리해야 합니다. 이벤트 처리를 위해서 Activity클래스에 android.viewView.OnTouchListener 인터페이스를 상속한 후, onTouch() 메서드를 재정의 합니다.
package kr.pe.hoyanet.viewflipperex;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ViewFlipper;
public class MainActivity extends Activity implements OnTouchListener {
/** ViewFlipper 컴포넌트 객체 */
private ViewFlipper m_viewFlipper;
/** ViewFilpper 안에서 터치된 X축의 좌표값 */
private int m_nPreTouchPosX = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
m_viewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper1);
m_viewFlipper.setOnTouchListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
}
애니메이션을 구현하기 위한 메서드 추가
Activity 클래스에 애니메이션을 구현하기 위한 다음의 두개의 메서드를 추가합니다.
private void MoveNextView() {
m_viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.appear_from_right));
m_viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
R.anim.disappear_to_left));
m_viewFlipper.showNext();
}
private void MovewPreviousView() {
m_viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.appear_from_left));
m_viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
R.anim.disappear_to_right));
m_viewFlipper.showPrevious();
}
이벤트 핸들러 안에서 슬라딩 처리 구현
이벤트를 정의하는 과정에서 추가된 onTouch() 메서드 안에 다음의 코드를 구현해 놓습니다. 화면이 터치되고 떨어지는 과정에서 터치된 지점의 X좌표 변확량을 판단하여 구동할 애니메이션을 결정하는 코드 입니다. 이 작업에서 주의할 점은, Eclipse에서 자동으로 추가한 기본 메서드는 리턴값이 false인데, 여기서는 true를 리턴한다는 점 입니다.
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
m_nPreTouchPosX = (int) event.getX();
}
if (event.getAction() == MotionEvent.ACTION_UP) {
int nTouchPosX = (int) event.getX();
if (nTouchPosX < m_nPreTouchPosX) {
MoveNextView();
} else if (nTouchPosX > m_nPreTouchPosX) {
MovewPreviousView();
}
m_nPreTouchPosX = nTouchPosX;
}
return true;
}
자식 화면을 ViewFlipper에 추가
이제 화면에 추가할 자식화면용 XML을 Activity안에서 별도로 로드하여 ViewFlipper 객체에게 addView() 메서드를 사용하여 추가해 줍니다.
m_viewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper1);
m_viewFlipper.setOnTouchListener(this);
// ViewFlipper에 서브 레이아웃 추가
LinearLayout sub1 = (LinearLayout) View.inflate(this,
R.layout.sub_view1, null);
LinearLayout sub2 = (LinearLayout) View.inflate(this,
R.layout.sub_view2, null);
LinearLayout sub3 = (LinearLayout) View.inflate(this,
R.layout.sub_view3, null);
m_viewFlipper.addView(sub1);
m_viewFlipper.addView(sub2);
m_viewFlipper.addView(sub3);
전체 코드
완성된 Activity의 전체 소스는 다음과 같습니다.
package kr.pe.hoyanet.viewflipperex;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;
import android.widget.ViewFlipper;
public class MainActivity extends Activity implements OnTouchListener {
/** ViewFlipper 컴포넌트 객체 */
private ViewFlipper m_viewFlipper;
/** ViewFilpper 안에서 터치된 X축의 좌표 */
private int m_nPreTouchPosX = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
m_viewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper1);
m_viewFlipper.setOnTouchListener(this);
// ViewFlipper에 서브 레이아웃 추가
LinearLayout sub1 = (LinearLayout) View.inflate(this,
R.layout.sub_view1, null);
LinearLayout sub2 = (LinearLayout) View.inflate(this,
R.layout.sub_view2, null);
LinearLayout sub3 = (LinearLayout) View.inflate(this,
R.layout.sub_view3, null);
m_viewFlipper.addView(sub1);
m_viewFlipper.addView(sub2);
m_viewFlipper.addView(sub3);
}
private void MoveNextView() {
m_viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.appear_from_right));
m_viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
R.anim.disappear_to_left));
m_viewFlipper.showNext();
}
private void MovewPreviousView() {
m_viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.appear_from_left));
m_viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
R.anim.disappear_to_right));
m_viewFlipper.showPrevious();
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
m_nPreTouchPosX = (int) event.getX();
}
if (event.getAction() == MotionEvent.ACTION_UP) {
int nTouchPosX = (int) event.getX();
if (nTouchPosX < m_nPreTouchPosX) {
MoveNextView();
} else if (nTouchPosX > m_nPreTouchPosX) {
MovewPreviousView();
}
m_nPreTouchPosX = nTouchPosX;
}
return true;
}
}
실행결과 확인
ViewFlipper의 영역 안을 손가락으로 슬라이딩 처리하면 화면이 전환되는 것을 확인할 수 있습니다.
=======================
=======================
=======================
일반적인 인터넷 검색을 통해 드래그를 이용한 화면 전환 구현이라 검색해보면,
ViewFlipper 과 Animation 효과를 이용한 화면 전환되는 소스들이 매우 많이있다.
헌데 이 방식은 드래그와 동시에 화면전환이 되지 않는다!
일단 드래그를 한후에 손을 화면에서 떼는 순간 그제서야 화면전환이되는... 반응성 제로인.. 그런 UI..
인터넷에서 드래그 화면전환을 검색하는 대부분의 사람들은 그런걸 원한게 아니라.. (개인적인 생각으로는)
구글플레이나 안드로이드 런처의 바탕화면 처럼 터치드래그와 동시에 화면이 움직이는
화사고 미려하고 아름다우며, 주인의 말을 찰떡같이 알아듣는 바둑이와 같은 UI를 원했을꺼라 생각된다.
물론 나도 그런걸 원해서 처음 검색을 시작하게 됬었고, 처음에는 적당히 타협해서 viewflipper 를 사용하긴 했었다.
뭐.. 여차저차해서 구현되어 있는 소스를 구하긴 구했지만.. 이해력 부족으로 뭐가 뭔지 매우 복잡해서 잠시 닫아놨었는데..
시간이 널널한 지금 이때에! 기회가 되서 하나하나 뜯어보고 주요 클래스와 메서드들을 알아보자는 의미로 구현해 보았다.
소스의 핵심 클래스는 Scroller !!!!
스크롤 지점만 등록해두면 알아서 스크롤이 되는것을 도와주는 친절한 클래스. ( 물론 손을 좀 보긴해야하지만.. )
아래 소스는 붙여넣기 하면 별다른 문제없이 실행 가능하고. (일부 리소스는 변경해줘야함)
실제 활용되기보다는 동작 구현의 중점을 두고 이해를 목적으로 작성하였다.
참고한 소스는 http://code.google.com/p/andro-views/
으.. 글을 작성하고 보니.. 이전 글들과 다르게 소스가 분량이 조금 많아서 그런지.. 소스코드 보기가 어렵다..
네이버에서 활용할수 있는 하이라이터를 썻는데 별로 맘에 들지가.. 티스토리로 블로그를 옴기던가 해야지... 이런..
1. Activity 클래스
package com.hdh.test.slidingview;
import android.app.Activity;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
public class main extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SlidingView sv = new SlidingView(this);
View v1 = View.inflate(this, R.layout.t1, null);
View v2 = View.inflate(this, R.layout.t2, null);
sv.addView(v1);
sv.addView(v2);
setContentView(sv);
}
}
2. 제스쳐에 따라 움직이는 뷰 클래스
package com.hdh.test.slidingview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
import android.widget.Toast;
/* 여러 화면을 구현할때 여러개의 엑티비티가 붙는것아니라
* viewgroup의 childview 로 넣어두고 스크롤을 통해 화면 전환 효과를 이루는 방식이다.
* 따라서 viewgroup 을 상속받고 화면에 넣고자하는 layout은 addview() 클래스를 통해 삽입한다.
*/
public class SlidingView extends ViewGroup {
private static final String TAG = "SlidingView";
// 드래그 속도와 방향을 판단하는 클래스
private VelocityTracker mVelocityTracker = null;
// 화면 전환을 위한 드래그 속도의 최소값 pixel/s (100 정도으로 속도로 이동하면 화면전환으로 인식)
private static final int SNAP_VELOCITY = 100;
/* 화면에 대한 터치이벤트가 화면전환을 위한 터치인가? 현 화면의 위젯동작을 위한
터치인가? 구분하는 값 (누른상태에서 10px 이동하면 화면 이동으로 인식) */
private int mTouchSlop = 10;
private Bitmap mWallpaper = null; // 배경화면을 위한 비트맵
private Paint mPaint = null;
/* 화면 자동 전황을 위한 핵심 클래스 ( 화면 드래그후 손을 뗏을때
화면 전환이나 원래 화면으로 자동으로 스크롤 되는 동작을 구현하는 클래스) */
private Scroller mScroller = null;
private PointF mLastPoint = null; // 마지막 터치 지점을 저장하는 클래스
private int mCurPage = 0; // 현재 화면 페이지
private int mCurTouchState; // 현재 터치의 상태
private static final int TOUCH_STATE_SCROLLING = 0; // 현재 스크롤 중이라는 상태
private static final int TOUCH_STATE_NORMAL = 1; // 현재 스크롤 상태가 아님
private Toast mToast;
public SlidingView(Context context) {
super(context);
init();
}
public SlidingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SlidingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mWallpaper = BitmapFactory.decodeResource(getResources(),
R.drawable.background_black_1280x1024); // 배경화면 불러오기
mPaint = new Paint();
mScroller = new Scroller(getContext()); // 스크롤러 클래스 생성
mLastPoint = new PointF();
}
// 차일드뷰의 크기를 지정하는 콜백 메서드
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d(TAG, "onMeasure");
for (int i = 0; i < getChildCount(); i++) {
// 각 차일드뷰의 크기는 동일하게 설정
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
}
// 차일드뷰의 위치를 지정하는 콜백 메서드
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d(TAG, "onLayout");
// 핵심 구현 부분으로써
// 차일드뷰들을 겹치지 않게 옆으로 차례대로 나열해서 배치한다.
// 옆으로 차례대로 배치를 해놔야 스크롤을 통해 옆으로 이동하는것이 가능해진다.
for (int i = 0; i < getChildCount(); i++) {
int child_left = getChildAt(i).getMeasuredWidth() * i;
getChildAt(i).layout(child_left, t, child_left + getChildAt(i).getMeasuredWidth(),
getChildAt(i).getMeasuredHeight());
}
}
// viewgroup 클래스의 onDraw 메서드라 생각하면 되겠다.
@Override
protected void dispatchDraw(Canvas canvas) {
canvas.drawBitmap(mWallpaper, 0, 0, mPaint); // 바탕화면을 그리고
for (int i = 0; i < getChildCount(); i++) {
drawChild(canvas, getChildAt(i), 100); // 차일드 뷰들을 하나하나 그린다.
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "event Action : " + event.getAction());
if (mVelocityTracker == null)
mVelocityTracker = VelocityTracker.obtain();
// 터치되는 모든 좌표들을 저장하여, 터치 드래그 속도를 판단하는 기초를 만듬
mVelocityTracker.addMovement(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 현재 화면이 자동 스크롤 중이라면 (ACTION_UP 의 mScroller 부분 참조)
if (!mScroller.isFinished()) {
mScroller.abortAnimation(); // 자동스크롤 중지하고 터치 지점에 멈춰서잇을것
}
mLastPoint.set(event.getX(), event.getY()); // 터치지점 저장
break;
case MotionEvent.ACTION_MOVE:
// 이전 터치지점과 현재 터치지점의 차이를 구해서 화면 스크롤 하는데 이용
int x = (int) (event.getX() - mLastPoint.x);
scrollBy(-x, 0); // 차이만큼 화면 스크롤
invalidate(); // 다시 그리기
mLastPoint.set(event.getX(), event.getY());
break;
case MotionEvent.ACTION_UP:
// pixel/ms 단위로 드래그 속도를 구할것인가 지정 (1초로 지정)
// onInterceptTouchEvent 메서드에서 터치지점을 저장해둔 것을 토대로 한다.
mVelocityTracker.computeCurrentVelocity(1000);
int v = (int) mVelocityTracker.getXVelocity(); // x 축 이동 속도를 구함
int gap = getScrollX() - mCurPage * getWidth(); // 드래그 이동 거리 체크
Log.d(TAG, "mVelocityTracker : " + v);
int nextPage = mCurPage;
// 드래그 속도가 SNAP_VELOCITY 보다 높거니 화면 반이상 드래그 했으면
// 화면전환 할것이라고 nextPage 변수를 통해 저장.
if ((v > SNAP_VELOCITY || gap < -getWidth() / 2) && mCurPage > 0) {
nextPage--;
} else if ((v < -SNAP_VELOCITY || gap > getWidth() / 2)
&& mCurPage < getChildCount() - 1) {
nextPage++;
}
int move = 0;
if (mCurPage != nextPage) { // 화면 전환 스크롤 계산
// 현재 스크롤 지점에서 화면전환을 위해 이동해야하는 지점과의 거리 계산
move = getChildAt(0).getWidth() * nextPage - getScrollX();
} else { // 원래 화면 복귀 스크롤 계산
// 화면 전환 하지 않을것이며 원래 페이지로 돌아가기 위한 이동해야하는 거리 계산
move = getWidth() * mCurPage - getScrollX();
}
// 핵심!! 현재 스크롤 지점과 이동하고자 하는 최종 목표 스크롤 지점을 설정하는 메서드
// 현재 지점에서 목표 지점까지 스크롤로 이동하기 위한 중간값들을 자동으로 구해준다.
// 마지막 인자는 목표 지점까지 스크롤 되는 시간을 지정하는 것. 밀리세컨드 단위이다.
// 마지막 인자의 시간동안 중간 스크롤 값들을 얻어 화면에 계속 스크롤을 해준다.
// 그러면 스크롤 애니메이션이 되는것처럼 보인다. (computeScroll() 참조)
mScroller.startScroll(getScrollX(), 0, move, 0, Math.abs(move));
if (mToast != null) {
mToast.setText("page : " + nextPage);
} else {
mToast = Toast.makeText(getContext(), "page : " + nextPage,
Toast.LENGTH_SHORT);
}
mToast.show();
invalidate();
mCurPage = nextPage;
// 터치가 끝났으니 저장해두었던 터치 정보들 삭제하고
// 터치상태는 일반으로 변경
mCurTouchState = TOUCH_STATE_NORMAL;
mVelocityTracker.recycle();
mVelocityTracker = null;
break;
}
return true;
}
// 완전 핵심!! 인 콜백 메서드. 스크롤 될때마다 무조건 계속 실행됨.
@Override
public void computeScroll() {
super.computeScroll();
// onTouchEvent 에서 지정된 mScroller 의 목표 스크롤 지점으로 스크롤하는데, 필요한 중간 좌표 값들을
// 얻기 위한 메서드로서, 중간 좌표값을 얻을수 있으면 true 를 리턴
if (mScroller.computeScrollOffset()) {
// 값을 얻을수 있다면. getCurrX,getCurrY 을 통해 전달되는데,
// 이는 목표 지점으로 스크롤하기 위한 중간 좌표값들을 Scroller 클래스가 자동으로 계산한 값이다.
// scrollTo() 를 통해 화면을 중간 지점으로 스크롤 하고,
// 앞서 말했듯 스크롤이 되면 자동으로 computeScroll() 메서드가 호출되기 때문에
// 목표 스크롤 지점에 도착할떄까지 computeScroll() 메서드가 호출되고 스크롤 되고 호출되고 반복.
// 따라서 화면에 스크롤 애니메이션을 구현된것처럼 보이게 됨.
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
// ViewGroup 의 childview 들에게 터치이벤트를 줄것인지 아니면 본인에게 터치이벤트를 줄것인지
// 판단하는 콜백 메서드 ( 터치 이빈트 발생시 가장먼저 실행 됨 )
// 리턴값으로 true 를 주게 되면 viewgroup의 onTouchEvent 메서드가 실행되고
// false 를 주면 ViewGroup 의 onTouchEvent은 실행되지 않고 childview 에게
// 터치 이벤틀르 넘겨주게 된다. 따라서, 화전 전환 할것인가? 차일드뷰의 버튼이나 여타 위젯을 컨트롤
// 하는 동작인가? 를 구분하는 로직이 여기서 필요하다.
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(TAG, "onInterceptTouchEvent : " + ev.getAction());
int action = ev.getAction();
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Scroller가 현재 목표지점까지 스크롤 되었지는 판단하는 isFinished() 를 통해
// 화면이 자동 스크롤 되는 도중에 터치를 한것인지 아닌지를 확인하여,
// 자식에게 이벤트를 전달해 줄건지를 판단한다.
mCurTouchState = mScroller.isFinished() ? TOUCH_STATE_NORMAL
: TOUCH_STATE_SCROLLING;
mLastPoint.set(x, y); // 터치 지점 저장
break;
case MotionEvent.ACTION_MOVE:
// 자식뷰의 이벤트인가 아니면 화면전환 동작 이벤트를 판단하는 기준의 기본이 되는
// 드래그 이동 거리를 체크 계산한다.
int move_x = Math.abs(x - (int) mLastPoint.x);
// 만약 처음 터치지점에서 mTouchSlop 만큼 이동되면 화면전환을 위한 동작으로 판단
if (move_x > mTouchSlop) {
mCurTouchState = TOUCH_STATE_SCROLLING; // 현재 상태 스크롤 상태로 전환
mLastPoint.set(x, y);
}
break;
}
// 현재 상태가 스크롤 중이라면 true를 리턴하여 viewgroup의 onTouchEvent 가 실행됨
return mCurTouchState == TOUCH_STATE_SCROLLING;
}
}
# 기능을 일부 수정한 버전 입니다.
위 소스에서 아래 소스로 onTouchEvent() 를 수정하고 underScroll(), overScroll 메서드를 추가하면, 페이지 무한 루프 기능 가능.
첫번째 페이지와 마지막 페이지가 연결되어 링형을 이루는 구조입니다.
// 추가
boolean isFirstMove = true; // ACTION_MOVE 의 제일 처음 이벤트만 동작하도록 하는 멤버 변수
// 수정
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "event Action : " + event.getAction());
if (mVelocityTracker == null)
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mLastPoint.set(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
int x = (int) (event.getX() - mLastPoint.x);
scrollBy(-x, 0);
// move 스크롤 이벤트가 가장 처음 일어났을때만
if (isFirstMove && getChildCount() > 2) {
// 현재 전체 페이지보다 상위로 스크롤 했을때, overScroll 이벤트 발생
if (x < 0 && mCurPage == getChildCount() - 1) {
overScroll();
} else if (x > 0 && mCurPage == 0) {
// 하위로 스크롤 했을때 underScroll
underScroll();
}
}
isFirstMove = false;
invalidate();
mLastPoint.set(event.getX(), event.getY());
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000);
int v = (int) mVelocityTracker.getXVelocity();
int gap = getScrollX() - mCurPage * getWidth();
Log.d(TAG, "mVelocityTracker : " + v);
int nextPage = mCurPage;
if ((v > SNAP_VELOCITY || gap < -getWidth() / 2) && mCurPage > 0) {
nextPage--;
} else if ((v < -SNAP_VELOCITY || gap > getWidth() / 2) && mCurPage < getChildCount() - 1) {
nextPage++;
}
int move = 0;
if (mCurPage != nextPage) {
move = getChildAt(0).getWidth() * nextPage - getScrollX();
} else {
move = getWidth() * mCurPage - getScrollX();
}
mScroller.startScroll(getScrollX(), 0, move, 0, Math.abs(move));
if (mToast != null) {
mToast.setText("page : " + nextPage);
} else {
mToast = Toast.makeText(getContext(), "page : " + nextPage, Toast.LENGTH_SHORT);
}
mToast.show();
invalidate();
mCurPage = nextPage;
mCurTouchState = TOUCH_STATE_NORMAL;
mVelocityTracker.recycle();
mVelocityTracker = null;
isFirstMove = true; // move 스크롤 체크용 초기화
break;
}
return true;
}
// 추가
private void underScroll() {
// 마지막 페이지를 가장 처음페이지 앞으로 이동 시킴
View v = getChildAt(getChildCount() - 1);
removeViewAt(getChildCount() - 1);
addView(v, 0);
setPage(getChildCount() - 1);
}
// 추가
private void overScroll() {
// 첫번재 페이지를 가장 마지막 페이지 뒤로 이동시킴
View v = getChildAt(0);
removeViewAt(0);
setPage(0);
addView(v);
}
// 추가
public void setPage(int p) {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
if (p < 0 || p > getChildCount())
return;
mCurPage = p;
scrollTo(getWidth() * p, 0);
}
[출처] [코드] 드래그를 통한 화면 전환 효과 구현|작성자
=======================
=======================
=======================
츌처: http://albbu.tistory.com/110
안드로이드 메인 화면이나 아이폰 메인 화면에서 좌우로 드래그 했을 때 좌우에서 화면이 나타나는 효과를 보여준다.
필요할 곳이 있어서 그것 흉내를 내보았다.
단, 화면 이동은 손가락을 화면에서 떼었을 때 발생한다.
손가락을 떼지 않은 상태에서 화면이 걸쳐있는 것은 아직 안해봤음.
1. 이벤트 처리
일단, 이러한 Activiy화면 변경과 관련해서 별다른 이벤트가 있을 줄 알았는데, 찾지 못하고,
onTouch 와 onClick을 함께 사용하였다.
onTouchListener를 달았을 때 구현해야 하는 onTouch 메쏘드는 parameter로 View 와MotionEvent를 넘겨받는데, 이 MotionEvent 에는 DOWN, UP, MOVE 등의 값이 정의되어 있어, 넘겨받는 이벤트의 action이 무엇인지에 따라 처리가 가능하다.
그런데 일단 급하게 대충 해서 그런지, onTouchListener만 달고, 로그를 확인해보면, DOWN 은 넘겨받는데, MOVE나 UP은 전달받지를 못하고 있었다.
(해당 리스너를 달아놓은 view는 Activity의 배경이 되는 FrameLayout 에 달아놓았는데, 그래서 그런건가...?)
그래서 아무 생각 없이 일단 onClickListener를 달았더니, 어? MOVE나 UP의 action을 받는다..
일단 이벤트는 잘 받으니 됐고...
이벤트 받았을 떄 화면을 바꾸어주는건 이렇게 했다.
mFrame.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
float distance = 0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 손가락을 touch 했을 떄 x 좌표값 저장
pressedX = event.getX();
break;
case MotionEvent.ACTION_UP:
// 손가락을 떼었을 때 저장해놓은 x좌표와의 거리 비교
distance = pressedX - event.getX();
break;
}
// 해당 거리가 100이 되지 않으면 이벤트 처리 하지 않는다.
if (Math.abs(distance) < 100) {
return false;
}
if (distance > 0) {
// 손가락을 왼쪽으로 움직였으면 오른쪽 화면이 나타나야 한다.
Intent intent = new Intent(mContext, RightActivity.class);
startActivity(intent);
} else {
// 손가락을 오른쪽으로 움직였으면 왼쪽 화면이 나타나야 한다.
Intent intent = new Intent(mContext, LeftActivity.class);
startActivity(intent);
}
finish(); // finish 해주지 않으면 activity가 계속 쌓인다.
return true;
}
});
2. 애니메이션 효과
이제 애니메이션 효과를 넣어서 화면의 좌우에서 activity가 미끄러지듯 넘어오도록 해보자.
activity 화면 이동 animation은 xml 파일을 추가한다.
왼쪽에서 화면이 나타나도록 할 경우, 기존에 있던 화면은 왼쪽으로 밀려 사라지도록 해야 한다.
그러므로 왼쪽에서 나타나는 애니메이션과 왼쪽으로 사라지는 애니메이션 두개의 xml 파일이 필요하다. (파일은 res폴더 아래에 anim 폴더를 만들고 그 안에 만들었다.)
우선 왼쪽에서 나타나는 xml 파일(left_in.xml)의 경우
<?xml version="1.0" encoding="UTF-8"?>
<!-- left in animation -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="100%p" android:toXDelta="0" android:duration="300" />
</set>
나타날 화면의 x좌표가 화면의 오른쪽 끝 (100%p)에서 왼쪽 끝(0)으로 이동하게 한다.
duration 값을 조절하면 화면 이동 속도를 조절할 수 있다.
화면이 왼쪽으로 사라지는 애니메이션 파일(left_out.xml)도 마찬가지 이다.
<?xml version="1.0" encoding="UTF-8"?>
<!-- left out animation -->
<translate android:fromXDelta="0" android:toXDelta="-100%p" android:duration="300" />
</set>
왼쪽 위 좌표가 0 에서 화면 바깥인 -100%p로 이동하게 하면 된다.
이런 식으로 right_in.xml과 right_out.xml도 만든다.
이제 이 애니메이션들을 적용시켜야 한다.
위 에 작성한 java 코드 에 다음 두 줄을 추가하고 확인해보면 된다.
if (distance > 0) {
Intent intent = new Intent(mContext, RightActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.left_in, R.anim.left_out);
} else {
Intent intent = new Intent(mContext, LeftActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.right_in, R.anim.right_out);
}
finish();
http://albbu.tistory.com/110 이놈에 이어서...
이번에는 안드로이드에서 기본적으로 제공하는 ViewFlipper를 사용해서 똑같은 기능을 적용해 보겠다.
달아주는 리스너에는 큰 차이가 없으나, 이전과 달리 화면마다 Activity 를 만들고 finish 할 필요가 없어 관리하기 좋다.
ViewFlipper는 넣고자 하는 view 들을 add 시켜서 사용한다.
add 할 때는 xml 파일에 넣어도 되고, java 코드로 넣어도 된다.
보여주어야 할 화면 갯수가 바뀌는 경우에는 Java 코드로 사용하는 것이 좋다.
일단 ViewFlipper를 적용할 xml 파일을 만들어보자.
main_activity.xml
android:id="@+id/main_layout"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ViewFlipper android:id="@+id/main_view_flipper"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<!-- ViewFlipper에서 보여줄 view 들 인덱스는 맨 위엣놈이 0 -->
<include android:id="@+id/left_layout" layout="@layout/left_activity" />
<include android:id="@+id/center_layout" layout="@layout/center_activity" />
<include android:id="@+id/right_layout" layout="@layout/right_activity" />
</ViewFlipper>
</LinearLayout>
center_activity.xml, left_activity.xml, right_activity.xml은 각각 전체화면으로 (테스트니까 대충) 만들어놓았다.
이제 이벤트를 처리할 activity java 파일을 만들어보자
public class CenterActivity extends BaseActivity implements OnTouchListener,
OnClickListener {
private static final String TAG = "CenterActivity";
LinearLayout mFrame;
ViewFlipper vf;
float pressedX; // 손가락이 눌렸을 때 x 좌표
private Context mContext;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main_activity);
vf = (ViewFlipper)findViewById(R.id.main_view_flipper);
// vf.setDisplayedChild(index); // 특정 인덱스의 view 부터 보여줄 때 사용
mContext = CenterActivity.this;
mFrame = (LinearLayout) findViewById(R.id.main_layout);
mFrame.setOnTouchListener(this);
mFrame.setOnClickListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(TAG, "MotionEvent: " + event.getAction());
float distance = 0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
pressedX = event.getX();
break;
case MotionEvent.ACTION_UP:
distance = pressedX - event.getX(); // 손가락이 이동한 거리 계산
if (Math.abs(distance) > 100) {
// ViewFlipper에 animation을 등록하고, Activity를 start 시키는 대신 메쏘드를 호출해준다.
if (distance > 0) {
vf.setInAnimation(mContext, R.anim.left_in);
vf.setOutAnimation(mContext, R.anim.left_out);
vf.showNext();
} else {
vf.setInAnimation(mContext, R.anim.right_in);
vf.setOutAnimation(mContext, R.anim.right_out);
vf.showPrevious();
}
return true;
}
}
return false;
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
이렇게 하면 어제 만든 activity를 start, finish 시켰을때와 동일하게 동작한다.
=======================
=======================
=======================
출처: http://snowbora.tistory.com/404
아이폰이나 안드로이드 어플들을 사용하다보면, 화면을 왼쪽 또는 오른쪽으로 드래그했을때,
화면이 휙휙 슬라이딩 되는 것처럼 전환되는 것들을 볼 수 있습니다.
ViewFlipper은 이러한 효과를 더 손쉽게 사용할 수 있도록 제공해줍니다.
ViewFlipper를 사용한 예제와는 조금 다르지만, 이런 효과를 구현할 수 있습니다.
ViewFlipper은 FrameLayout을 상속받습니다.
FrameLayout은 AbsoluteLayout과 비슷하게 Layout안의 각각의 View 들이 서로 좌표를 겹쳐서 가집니다.
(나중에 그려진 View가 먼저 그려진 View를 덮어 버립니다.) 이 점을 주의해야 합니다.
View를 좌우로 밀었을 때의 애니메이션 효과를 미리 만들어줍니다.
이전에 다루었던 애니메이션 부분들과 어느 정도 이어집니다.
[안드로이드] Layout을 통해 Tween Animation 구현하기 (투명도 조절) - http://snowbora.com/390[안드로이드] Layout을 통해 Tween Animation 구현하기 (개체 움직임) - http://snowbora.com/391[안드로이드] Layout을 통해 Tween Animation 구현하기 (Set 태그) - http://snowbora.com/392
appear_from_left.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
android:fromXDelta="-100%p"
android:toXDelta="0%p"
android:duration="500"/>
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="500" />
</set>
appear_from_right.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
android:fromXDelta="100%p"
android:toXDelta="0%p"
android:duration="500"/>
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="500" />
</set>
disappear_to_left.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
android:fromXDelta="0%p"
android:toXDelta="-100%p"
android:duration="500"/>
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="500" />
</set>
disappear_to_right.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
android:fromXDelta="0%p"
android:toXDelta="100%p"
android:duration="500"/>
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="500" />
</set>
그리고 main.xml을 작성해봅니다.
<?xml version="1.0" encoding="utf-8"?>
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"/>
<ViewFlipper
android:id="@+id/viewFlipper"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="@+id/view1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:text="View 1"
android:textSize="30px"/>
<Button
android:id="@+id/view2"
android:src="@drawable/icon"
android:text="View 2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/view3"
android:src="@drawable/icon"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"/>
</ViewFlipper>
</LinearLayout>
마지막으로 actViewFlipper.java 파일에 다음과 같은 코드를 입력합니다.
package View.Flipper;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.widget.ViewFlipper;
public class actViewFlipper extends Activity {
private ViewFlipper m_viewFlipper;
private int m_nPreTouchPosX = 0;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
m_viewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper);
m_viewFlipper.setOnTouchListener(MyTouchListener);
}
private void MoveNextView()
{
m_viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.appear_from_right));
m_viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.disappear_to_left));
m_viewFlipper.showNext();
}
private void MovewPreviousView()
{
m_viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.appear_from_left));
m_viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.disappear_to_right));
m_viewFlipper.showPrevious();
}
View.OnTouchListener MyTouchListener = new View.OnTouchListener()
{
public boolean onTouch(View v, MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
m_nPreTouchPosX = (int) event.getX();
}
if (event.getAction() == MotionEvent.ACTION_UP)
{
int nTouchPosX = (int) event.getX();
if (nTouchPosX < m_nPreTouchPosX)
{
MoveNextView();
} else if (nTouchPosX > m_nPreTouchPosX)
{
MovewPreviousView();
}
m_nPreTouchPosX = nTouchPosX;
}
return true;
}
};
}
전체 프로젝트 파일을 첨부합니다.
- K2011.06.05 23:09
- snowbora2011.06.06 20:33
- 상하로 플리핑하는 건 어떤걸 말씀하시는 건가요?
글자수 많아서 위/아래 스크롤해야 할 때 스크롤 말씀하시는 건가요?
ViewFlipper로 상/하 플리핑 구현하는 것도 어렵지 않고, 그냥 ScrollView를 이용해서 위/아래 스크롤하는 것도 어렵지는 않습니다.
다만 일반적인 경우는 ScrollView를 이용하는 것이 더 편리할 거에요. 간단한 예제하나 만들어서 블로그에 올려보도록 하겠습니다. - snowbora2011.06.06 20:44
- http://snowbora.com/405 에 ScrollView에 대한 내용을 블로깅했습니다. 감사합니다.
- Viewfliper를 쓰면 좌우로 플릭킹이 가능한것을 보여주셨는데요 그럼 그 안에 있는 textview를 상하로 플릭킹할 수 있도록 구현하는 것도 가능한가요??
- K2011.06.07 07:33
- snowbora2011.06.07 21:57
- 그렇게 사용 가능합니다.
ViewFlipper에서 사용하는 후보 View 중 하나로 ScrollView를 사용하면 되죠. 그 안에서 TextView를 자식View로 구현하면 됩니다.
- 상하/좌우 다 스크롤할수 있는 방법을 알고싶어서 물어본거에요 viewfliper를 좌우스크롤하는데 쓰고 scrollview로 상하이렇게 같이써서 구현이 가능한지 알고싶습니다
- K2011.06.07 22:45
- 계속 질문 드리게되네요~ 얼마전에 dynamic viewflipper란 걸 듣게되었는데요 이게 혹시 viewfliper를 사용하면서 웹에서 데이터를 갖고와서 dynamic하게 view를 생성하고 append하는 걸 말하는 것인가요? 근데 만약에 웹데이터가 정말많은 경우라면 view를 append하는데 한계가 있을거 같은데 제가 이해하는게 맞는건인지 좀 헷갈리네요 쩝...
- K2011.06.08 13:31
- snowbora2011.06.12 20:18
- 저도 Dynamic View Flipper이 무엇인지 몰라서 찾아봤는데,
http://www.slideshare.net/dcgraham7/dynamic-view-flipper-2036378
상으로 볼 때는 View Flipper 안에 들어가는 자식 뷰를 동적으로 생성하고 동적으로 해제하는 것을 말하는 것 같네요.
그냥 View Flipper는 갖고 있는 뷰 안에서 계속 순환하면서 바꿔가는 건데, Dynamic View Flipper는 새로운 뷰를 계속 추가하거나 빼면서 바꿔갈 수 있는 것 같습니다. ^^;
- 계속 질문 드리게되네요~ 얼마전에 dynamic viewflipper란 걸 듣게되었는데요 이게 혹시 viewfliper를 사용하면서 웹에서 데이터를 갖고와서 dynamic하게 view를 생성하고 append하는 걸 말하는 것인가요? 근데 만약에 웹데이터가 정말많은 경우라면 view를 append하는데 한계가 있을거 같은데 제가 이해하는게 맞는건인지 좀 헷갈리네요 쩝...
- ssamture2011.06.09 16:59
- snowbora2011.06.12 20:19
- 슬라이드 터치를 하고 나면 화면이 좌우로 스크롤 되는 건 View Flipper과는 조금 성격이 다른 것 같습니다. 그냥 기본적인 FrameLayout 등을 만들고 그 안에서 ImageView의 좌표를 바꿔가면서 움직이게 해야 될 것 같네요. ^^;;
- 좋은 글 잘 봤습니다. 제가 찾던 내용이네요ㅠㅠ
그런데 올려주신 예제는 슬라이드 터치를 하고 손을 떼면 화면이 좌우로 스크롤 되는데요.
손을 따라서 스크롤(갤럭시 메인화면 또는 안드로이드 기본 어플 중 "뉴스 및 날씨" 등) 하는건 어떻게 구현을 해야 할까요?? - reneponette2011.09.24 15:01
- snowdeer2011.09.26 19:06
- 좋은 정보 감사합니다. ^^;
ViewPager 라는 건 처음 듣는데, 허니콤부터 추가된건가 보군요.
진저브레드에서 좌우 스크롤되는 갤러리 만드는라 고생 좀 했는데.. 좋은게 추가되나 보네요. ^^;;
(사실 진저브레드에서도 Gallery 사용하면 좀 수월하게 할 수도 있긴 합니다. ㅋ)
- 그냥 지나가가 들르게되어 댓글답니다. 좌우 flicking의 경우 현재는 ViewPager 라는 대안이 있습니다. 사실 대안이라기보다 이제부턴 권고하고 있는 방법입니다. 호환라이브러리 v4에 들어가 있구영... Fragment 라는 지금은 생소한 개념을 사용하긴 합니다만.... 허니콤부턴 이 개념이 대세기 때문에 지금부터라도 익숙해지는게 장땡이져.. 참고하세영~ ^^
- 괴무리2011.12.06 19:46
- 안녕하세요... 지나다가 좋은 정보 보고 갑니다. ^^
하나 질문할게 있어요 (__)
저는 뷰플리퍼에 커스텀 뷰를 여러개 두고 플리킹을 하려고합니다.
커스텀뷰를 올려서 플리킹이 되는거 까진 확인했는데
원하는 영역에서 ontouch영역이 적용이 되지 않더라고요. ㅠ
예를 들자면 전체화면에 ontouch영역을 나름 걸었는데.. 커스텀뷰안의 특정 뷰영역에서만 ontouch 이벤트가 걸리더라고요.
relative로도 해보고 linear로도 해봤는데..잘 안되네요 ㅎ;
혹시 해결 방법아시면 힌트 좀 부탁합니다. (__) - 홍의찬2012.03.15 09:22
- 좀 때가 지나긴 했지만,.. 질문하나 드립니다.
위젯으로 구현된 FrameLayout 안에 LinearLayout에 여러개의 TextView들이 있습니다..
그런 LinearLayout이 7개의 그룹이 존재한다고 했을때 각각의 LinearLayout을 ViewFliper 안에 두고 상하로 플릭킹 할 수 있을까요?
혹시 아직 보고 계시다면 답변좀 부탁드립니다... - 후덜덜2012.04.06 11:08
- 감사합니다 퍼갈께용
=======================
=======================
=======================
'스마트기기개발관련 > 안드로이드 개발' 카테고리의 다른 글
Android에서 외부 한글폰트 넣어 적용하기 관련 TextView 에 Font 폰트 바꾸기 관련 (0) | 2020.09.21 |
---|---|
안드로이드 작업 해상도, dip에 따른 폰트작업 관련 (0) | 2020.09.21 |
android 안드로이드 액션바 (ActionBar) 관련 (0) | 2020.09.20 |
안드로이드 android webview 에서 https 보안에서 컨텐츠(이미지, 텍스트)들이 error, 막히거나, 안보일때 방법 (0) | 2020.09.20 |
안드로이드 Android Studio 프로젝트 라이브러리파일(.jar) 추가하기 (0) | 2020.09.20 |