четверг, 13 июня 2013 г.

Создание горизонтального меню для Android-приложений

В этой статье я хочу поделиться одним из способов разработки красивого анимированного меню или текстового горизонтального списка. 



Идея

Создание горизонтального меню для Android-приложенийСоздание горизонтального меню для Android-приложений

Для реализации такого списка я использую элемент TextSwitcher. Этот элемент содержит в себе TextView, который мы можем кастомизировать по своему усмотрению. TextSwitcher полезен для анимированной смены текста.
Мы будем использовать 3 TextSwitcher. Основной  Switcher используется для идентификации текущего элемента в списке, а боковые – для возможности навигации и для отображения ближайших элементов.
Для эффекта затенения мы создадим градиентный TextView с переходом от прозрачного до нужного нам цвета.

Подготовка ресурсов

Для начала разработаем анимацию смены текста слева направо и справа налево.
Исходный код anim/push_left_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="300"
        android:fromXDelta="-100%p"
        android:toXDelta="0" />

    <alpha
        android:duration="300"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>

Исходный код anim/push_left_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="300"
        android:fromXDelta="0"
        android:toXDelta="-100%p" />

    <alpha
        android:duration="300"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />
</set>

Исходный код anim/push_right_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="300"
        android:fromXDelta="100%p"
        android:toXDelta="0" />

    <alpha
        android:duration="300"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>

Исходный код anim/push_right_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="300"
        android:fromXDelta="0"
        android:toXDelta="100%p" />

    <alpha
        android:duration="300"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />
</set>

Для градиентного TextView необходимо описать его свойства:
Исходный код values/attrs.xml
<resources>

    <declare-styleable name="GradientTextView">
        <attr name="colorStartGradient" format="integer" />
        <attr name="colorEndGradient" format="integer" />
    </declare-styleable>

</resources>

Так же добавим цвета для градиента:
Исходный код values/colors.xml
<resources>
    <color name="textview_start_gradient">#00ffffff</color>
       <color name="textview_end_gradient">#88ffffff</color>
</resources>

Разработаем основной макет окна, который будет содержать наше анимированное меню:
Исходный код layout/main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <TextSwitcher
        android:id="@+id/scoreboard_location_left"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <TextSwitcher
        android:id="@+id/scoreboard_location"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <TextSwitcher
        android:id="@+id/scoreboard_location_right"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

</LinearLayout>

TextSwitcher содержит в себе TextView. В нашем проекте будут использоваться два вида текстовых элементов управления: обычный (для центрального элемента) и градиентный (для боковых элементов).
Исходный код layout/central_textview.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:id="@+id/textview" />

Исходный код layout/gradient_textview.xml
<org.snowpard.proects.twenty.GradientTextView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/org.snowpard.proects.twenty"
    android:id="@+id/textview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    app:colorEndGradient="@color/textview_end_gradient"
    app:colorStartGradient="@color/textview_start_gradient" />

Разработка приложения


  • Создание градиентного TextView
public class GradientTextView extends TextView {

 private int colorStartGradient, colorEndGradient;
 private boolean direction;
 private LinearGradient gradient; 
 
 public GradientTextView(Context context, AttributeSet attrs) {
  super(context, attrs);

  TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.GradientTextView);
  colorStartGradient = a.getColor(R.styleable.GradientTextView_colorStartGradient, -2);
  colorEndGradient = a.getColor(R.styleable.GradientTextView_colorEndGradient, -2);
  direction = false;
 }
 
 public void setDirection(boolean direction)
 {
  this.direction = direction;
 }

 @Override
 protected void onDraw(Canvas canvas) {
  if (colorStartGradient != -2 && colorEndGradient != -2)
  {
   gradient = new LinearGradient(direction ? getWidth() : 0, 0, direction ? 0 : getWidth(), 0, colorStartGradient,
      colorEndGradient, TileMode.CLAMP);
    

   getPaint().setShader(gradient); 
   
  }
  super.onDraw(canvas);
 }

}

  • Разработка основного класса MainActivity, работа с горизонтальным меню.
Добавим в класс константы, которые будут отвечать за направления анимации.
public static final int DIRECTION_NONE  = 0;
public static final int DIRECTION_LEFT  = 1;
public static final int DIRECTION_RIGHT  = 2;

private int current_direction = DIRECTION_RIGHT;

Опишем элементы управления и анимацию, которые будем использовать для работы с меню:
private TextSwitcher switcher, switcher_left, switcher_right;
private Animation in_left, in_right, out_left, out_right;

И добавим в атрибуты класса массив айтемов для горизонтального меню и индекс для идентификации текущего айтема:
private String[] locations = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5"};
private int current_index;

Разработаем методы для работы с горизонтальным меню:
- setAnimation: для установки нужной анимации для смены текста
private void setAnimation(TextSwitcher switcher, Animation in, Animation out)
 {
  switcher.setInAnimation(in);
  switcher.setOutAnimation(out);
 }

- initSwitch: инициализация TextSwitcher
private void initSwitch(TextSwitcher switcher, final int place, Animation in, Animation out, final Context context)
 {  
  switcher.setFactory(new ViewFactory() {
   
   @Override
   public View makeView() {
    TextView view = (TextView)LayoutInflater.from(context).inflate((place == 0) ? R.layout.central_textview: R.layout.gradient_textview, null).findViewById(R.id.textview);
    if (view instanceof GradientTextView)
     ((GradientTextView)view).setDirection(place != 1);
    return view;
   }
  });
  setAnimation(switcher, in, out);
 }

- setSwitch: смена айтема в горизонтальном меню
private void setSwitch(int direction)
 {
  boolean isUpdate = false;
  if (direction == DIRECTION_LEFT && current_index > 0)
  {
   isUpdate = true;
   current_index--;
  }
  else if (direction == DIRECTION_RIGHT && current_index < locations.length - 1)
  {
   isUpdate = true;
   current_index++;
  } else if (direction == DIRECTION_NONE)
   isUpdate = true; 
  if (isUpdate)
  {
   if (direction != current_direction && direction != DIRECTION_NONE)
   {
    current_direction = direction;
    if (current_direction == DIRECTION_RIGHT)
    {
     setAnimation(switcher_left, in_right, out_left);
     setAnimation(switcher, in_right, out_left);
     setAnimation(switcher_right, in_right, out_left);
    } else 
    {
     setAnimation(switcher_left, in_left, out_right);
     setAnimation(switcher, in_left, out_right);
     setAnimation(switcher_right, in_left, out_right);
    }
   }
   
   if (current_index - 1 >= 0)
    switcher_left.setText(locations[current_index - 1]);
   else 
    switcher_left.setText("");
   
   switcher.setText(locations[current_index]);
   
   if (current_index + 1 <= locations.length - 1)
    switcher_right.setText(locations[current_index + 1]);
   else 
    switcher_right.setText("");
  }
 }

Теперь необходимо все инициализироваться в методе onCreate() нашего Activity:
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  in_left = AnimationUtils.loadAnimation(this,
                R.anim.push_left_in);
  in_right = AnimationUtils.loadAnimation(this,
                R.anim.push_right_in);
  out_right = AnimationUtils.loadAnimation(this,
                R.anim.push_right_out);
  out_left = AnimationUtils.loadAnimation(this,
                R.anim.push_left_out);
  
        switcher = (TextSwitcher) findViewById(R.id.scoreboard_location);
        switcher_left = (TextSwitcher) findViewById(R.id.scoreboard_location_left);
        switcher_right = (TextSwitcher) findViewById(R.id.scoreboard_location_right);
        
        initSwitch(switcher, 0, in_right, out_left, this);
        initSwitch(switcher_left, 1, in_right, out_left, this);
        initSwitch(switcher_right, 2, in_right, out_left, this);
        
        switcher_left.setOnClickListener(new OnClickListener() {
   
   @Override
   public void onClick(View v) {
    setSwitch(DIRECTION_LEFT);
   }
  });
        switcher_right.setOnClickListener(new OnClickListener() {
   
   @Override
   public void onClick(View v) {
    setSwitch(DIRECTION_RIGHT);
   }
  });
        
        setSwitch(DIRECTION_NONE);
 }

Ссылки


  • Исходные коды данного проекта можно скачать отсюда: zip