- прокручивать список от начала и до конца (но не по кругу), так чтобы выделять центральный элемент.
- по мере удаления элемента от центра компонента изменять шрифт и прозрачность цифр
- “доводить“ список до нужного элемента
- отображать заданное количество элементов на экране
- определять направление скроллинга (вверх или вниз)
- рисовать тень для содержимого текстовых окон
Должен получиться компонент подобного вида:
Унаследуем наш компонент RollView от LinearLayout с дочерним элементом ListView. Внутри компонента реализуем интерфейс OnScrollListener для определения поведения ListView при скроллинге.
public class RollView extends LinearLayout implements OnScrollListener{ private final ListView innerListView; }
В конструкторе инициализируем ListView через xml файл и присваиваем слушателя.
Для представления данных создадим внутренний адаптер с переопределенным методом getView():
private class RollAdapter extends ArrayAdapter<String> { private final LayoutInflater mInflater; @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null){ convertView = mInflater.inflate(R.layout.roll_view_adapter, null); convertView.setLayoutParams(mParams); } TextView tv = (TextView) convertView.findViewById(R.id.text); tv.setTag(position); // записываем позицию элемента tv.setText(getItem(position)); convertView.setTag(tv); //записываем ссылку на TextView в тег if (!listViews.contains(convertView)) listViews.add(convertView); // в список для последующего обновления размера текста return convertView; } }
Все View из метода getView будем записывать в ArrayList, чтобы изменять их параметры. Метод refreshLayoutParams() задает размеры для элементов списка в зависимости от количества видимых элементов. Больше в классе адаптера ничего делать не будем.
Для того, чтобы можно было сдвинуть первый элемент списка в середину добавим в начало и конец массива пустые строки.
Теперь нужно обработать скроллинг в методах onScroll и onScrollStateChanged:
private int lastFirstVisibleElement; // индекс предыдущего "первого видимого элемента" для определения направления скроллинга private int centralIndex; //индекс элемента находящегося в центре @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { refreshTextViews(); //обновление размера текста и прозрачности //Для определения направления скроллинга if (lastFirstVisibleElement > firstVisibleItem){ Log.i("RollView", "Scroll up"); } else if (lastFirstVisibleElement < firstVisibleItem){ Log.i("RollView", "Scroll down"); } lastFirstVisibleElement = firstVisibleItem; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //После отпускания пальца if (scrollState == SCROLL_STATE_IDLE){ //Плавная доводка smoothScrollToPositionFromTop(centralIndex - totalElementVisible / 2 , 0, 1); }
Метод refreshTextViews() отвечает за изменение размера текста и прозрачности в зависимости от положения элемента:
public void refreshTextViews(){ float maxTextSize = 0; for (View v : listViews){ int centerOfViewY = v.getBottom() - (mAdapter.mParams.height / 2); ShadowTextView tv = (ShadowTextView) v.getTag(); float coefficient = (Math.abs(centerOfViewY - mAdapter.centerLineY)) / (float)mAdapter.centerLineY; float scale = 0; //Если коэффициент больше 1 - значит элемент за пределами видимости if (coefficient < 1) scale = Math.abs(coefficient - 1); tv.setAlpha(scale); //Определяем элемент с наибольшим размером текста для доводки к нему float textSize = CENTRAL_TEXT_SIZE * scale; if (textSize > maxTextSize){ maxTextSize = textSize; centralIndex = (Integer) tv.getTag(); } tv.setTextSize(textSize); } }
Осталось добавить тени для текста. Для этого создадим унаследованный от TextView компонент ShadowTextView. Для рисования текста с тенями нужно создать кисть(Paint) и задать ей параметры:
// Параметры кисти для рисования теней private void initPaint(){ mPaint.setAntiAlias(true); mPaint.setTextSize(getTextSize()); mPaint.setColor(Color.WHITE); mPaint.setStrokeWidth(2.0f); mPaint.setStyle(Paint.Style.FILL); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setShadowLayer(10.0f, 0.0f, 0.0f, Color.BLACK); }
и в методе onDraw() перерисовать компонент:
private final Rect mBounds = new Rect(); // границы текста @Override protected void onDraw(Canvas canvas){ canvas.drawColor(Color.TRANSPARENT); int x = getWidth() / 2; int y = (getHeight() + mBounds.height()) / 2; canvas.drawText(getText().toString(), x, y, mPaint); } }
Для перерисовки теней из RollView добавим метод redraw():
public void redraw(){ text = getText().toString(); mPaint.setTextSize(getTextSize()); mPaint.getTextBounds(text, 0, getText().toString().length() , mBounds); invalidate(); }
Осталось только заменить TextView в на ShadowTextView и вызвать в методе refreshTextViews метод tv.redraw();
Теперь для получения выбранного пользователем значения осталось только добавить методы getCurrentItemValue() и getCurrentItemIndex().
Наглядная демонстрация работы:
Ссылка на полный проект:
https://bitbucket.org/msinchevskaya/rollview
ссылка на оригинал статьи http://habrahabr.ru/post/225649/
Добавить комментарий