Вторник, 12.12.2017, 09:18
Поиск
Никнэйм
Сертификат на никнейм Olelucoye, зарегистрирован на Тимофеев Константин Михайлович
Зарегистрируй свой никнейм
Обратная связь
olelucoye.tk@yandex.ru
Реклама AdSense
Реклама
Друзья сайта
  • Лига медицинского права
  • Гостиница "Зай"
  • FAQ по системе
  • Инструкции для uCoz
  • Главная » Статьи » Андроид разработка

    Создание кастомного View-элемента интерфейса.

    Создадим собственный view-элемент – снова часы, но на этот раз стрелочные.

    Создаем обычный проект с активити MainActivity.java. В него мы будем помещать наш view-элемент. Само активити пока оставим в покое, займемся созданием собственно самой вьюшкой. Создадим новый класс WatchView.java наследующийся от класса SurfaceView. И напишем конструктор класса – пока только с вызовом конструктора родительского класса.

    public class WatchView extends SurfaceView {

                public WatchView(Context context, AttributeSet attrs) {

                            super(context, attrs);

                }

    }

    Сохраняем. Теперь, если переключиться на layout файл и посмотреть палитру элементов - в разделе Custom &Library Views увидим новый элемент WatchView.

    Его можно добавлять в файл макета как любой другой элемент из палитры. Добавим его сразу в макет основного активити activity_main.xml. Пока он ничего не отбражает

    <RelativeLayout

        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"
        tools:context="ru.ucoz.olelucoye.watchanalog.MainActivity" >

        <ru.ucoz.olelucoye.watchanalog.WatchView
            android:id="@+id/wvWatch"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:padding="0dp" />

    </RelativeLayout>

    Напишем код отрисовки часов в WatchView.java. Чтобы начать рисовать нам нужно узнать размеры, в которые должен вписаться рисунок. Для этого напишем метод, в который из главного активити будем сообщать вьюшке её размеры.

    public void setSizes(int x){
            size = x;
            center = x/2;
            offsetA = (int) Math.rint((size/2 - 15) * Math.sin(Math.toRadians(30)) - 4);
            offsetB = (int) Math.rint((size/2 - 15) * Math.sin(Math.toRadians(60)) - 4);
            lenA = size/45;
            lenB = (int)(lenA * 1.7);
            
        }

    Поскольку элемент должен быть квадратным, достаточно передавать один размер. Так же находим центр и вычисляем смещения, которые будем использовать при рисовании делений циферблата и стрелок. Перед рисованием объявим несколько кистей, три объекта Path для циферблата и стрелок, две матрицы, с помощью которых будем поворачивать стрелки, и SurfaceHolder, с помощью которого изображение переносится на SurfaceView. В конструкторе инициализируем все эти объекты.

    public class WatchView extends SurfaceView {
        Paint paint, paint_fill, paint2_fill;
        Path dial, hour_arrow, minute_arrow;
        int size, center, offsetA, offsetB, lenA, lenB;
        Matrix matrixHour, matrixMinute;
        int angleH, angleM;
        SurfaceHolder surfaceHolder;

        public WatchView(Context context, AttributeSet attrs) {
            super(context, attrs);
            surfaceHolder = getHolder();
            paint  = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setColor(Color.RED);
            paint.setStrokeWidth(3);
            paint.setStyle(Paint.Style.STROKE);
            paint_fill  = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint_fill.setColor(Color.RED);
            paint_fill.setStrokeWidth(3);
            paint_fill.setStyle(Paint.Style.FILL_AND_STROKE);
            paint2_fill  = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint2_fill.setColor(Color.BLUE);
            paint2_fill.setStrokeWidth(3);
            paint2_fill.setStyle(Paint.Style.FILL_AND_STROKE);
            dial = new Path();
            minute_arrow = new Path();
            hour_arrow = new Path();
            matrixHour = new Matrix();
            matrixMinute = new Matrix();
        }

     Отрисовка часов будет происходить в методе setTime также по команде из главного активити. Передавать в этот метод будем текущие значения часов и минут. По этим значениям вычисляем углы, на которые нужно повернуть стрелки, и настраиваем матрицы на поворот. В объекты Path помещаем алгоритмы прорисовки циферблата и стрелок - ничего сложного, если разобраться с системой координат: точка отсчета - левый верхний угол view-элемента (не экрана, а именно самой вьюшки), ось x направлена вправо, ось y - вниз.

    public void setTime(int h, int m){
            angleM = m * 6;
            angleH = (int)(h*30 + m/2);
            
            matrixMinute.reset();
            matrixMinute.setRotate(angleM, center, center);
            
            matrixHour.reset();
            matrixHour.setRotate(angleH, center, center);
            
            dial.reset();
            dial.addCircle(center, center, size/2 -15, Path.Direction.CW);
            dial.moveTo(15, center);
            dial.lineTo(lenA*5, center);
            dial.moveTo(size - 15, center);
            dial.lineTo(size - lenA*5, center);
            dial.moveTo(center, 15);
            dial.lineTo(center, lenA*5);
            dial.moveTo(center, size - 15);
            dial.lineTo(center, size - lenA*5);
            dial.moveTo(center + offsetA, center - offsetB);
            dial.lineTo(center + offsetA - lenA, center - offsetB + lenB);
            dial.moveTo(center + offsetB, center - offsetA);
            dial.lineTo(center + offsetB - lenB, center - offsetA + lenA);
            dial.moveTo(center + offsetB, center + offsetA);
            dial.lineTo(center + offsetB - lenB, center + offsetA - lenA);
            dial.moveTo(center + offsetA, center + offsetB);
            dial.lineTo(center + offsetA - lenA, center + offsetB - lenB);
            dial.moveTo(center - offsetA, center + offsetB);
            dial.lineTo(center - offsetA + lenA, center + offsetB - lenB);
            dial.moveTo(center - offsetB, center + offsetA);
            dial.lineTo(center - offsetB + lenB, center + offsetA - lenA);
            dial.moveTo(center - offsetB, center - offsetA);
            dial.lineTo(center - offsetB + lenB, center - offsetA + lenA);
            dial.moveTo(center - offsetA, center - offsetB);
            dial.lineTo(center - offsetA + lenA, center - offsetB + lenB);
                    
            hour_arrow.reset();
            hour_arrow.moveTo(center, center);
            hour_arrow.lineTo(center - (int)(lenA*1.5), center - (int)(lenA*1.5));
            hour_arrow.lineTo(center , size/4);
            hour_arrow.lineTo(center + (int)(lenA*1.5), center - (int)(lenA*1.5));
            hour_arrow.close();
            minute_arrow.reset();
            minute_arrow.moveTo(center, center);
            minute_arrow.lineTo(center - (int)(lenA*1.5), center - (int)(lenA*1.5));
            minute_arrow.lineTo(center , size/6);
            minute_arrow.lineTo(center + (int)(lenA*1.5), center - (int)(lenA*1.5));
            minute_arrow.close();
            
            minute_arrow.transform(matrixMinute);
            hour_arrow.transform(matrixHour);
            Canvas canvas = surfaceHolder.lockCanvas();
            canvas.drawColor(Color.WHITE);
            canvas.drawPath(dial, paint);
            canvas.drawPath(hour_arrow, paint_fill);
            canvas.drawPath(minute_arrow, paint2_fill);
            surfaceHolder.unlockCanvasAndPost(canvas);
        }

    Затем применяем матрицы к стрелкам, наносим Path'ы на Canvas и выставляем его на всеобщее обозрение.

    Теперь можно заняться MainActivity.java. В onCreate получаем размеры экрана. По ним вычисляем размеры элемента WatchView. С помощью LayoutParams придаем ему нужные размеры, а также передаем размер в метод setSize.

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Display display = getWindowManager().getDefaultDisplay();
            DisplayMetrics metricsB = new DisplayMetrics();
            display.getMetrics(metricsB);
            screenW = metricsB.widthPixels;
            screenH = metricsB.heightPixels;
            setContentView(R.layout.activity_main);
            sizeGage = screenW - 10;
            if(screenW > screenH) sizeGage = screenH - 10;
            watch = (WatchView) findViewById(R.id.wvWatch);
            RelativeLayout.LayoutParams wparam = (RelativeLayout.LayoutParams) watch.getLayoutParams();
            wparam.width = sizeGage;
            wparam.height = sizeGage;
            watch.setLayoutParams(wparam);
            watch.setSizes(sizeGage);
    }

    В onResume с помощью таймера запускаем с задержкой 500 мс метод UpdateTime, в котором будем получать текущее время и передавать его в WatchView. Для чего нужна задержка - во время вызова onResume, макет активити еще не отображен на экране. Если вызвать UpdateTime сразу приложение выдаст ошибку. Поэтому мы даем время приложению вывести элементы макета на экран. Задержка  
        @Override
        protected void onResume(){
            new Timer().schedule(new TimerTask(){

                @Override
                public void run() {
                    UpdateTime();
                }
            }, 500);
            super.onResume();
        }

    Код самого метода UpdateTime:

    private void UpdateTime(){
            c = Calendar.getInstance();
            int iHour = c.get(Calendar.HOUR_OF_DAY);
            int iMin = c.get(Calendar.MINUTE);
            watch.setTime(iHour, iMin);
         }

    Можно уже попробовать запустить программу на эмуляторе. Все работает, только стрелки не ходят. Чтобы стрелки двигались нам нужно ежеминутно вызывать метод UpdateTime. Для этого сделаем ресивер и настроим его на отлов события TIME_TICK, которое происходит ежеминутно.

    class MinuteUpdate extends BroadcastReceiver{
            @Override
            public void onReceive(Context context, Intent intent) {
                UpdateTime();
            }
        }

    В onCreate добавим строки

    minuteUpdate = new MinuteUpdate();
    registerReceiver(minuteUpdate, new IntentFilter("android.intent.action.TIME_TICK"));

    Не забываем удалить ресивер при выходе из приложения

       @Override
        protected void onDestroy(){
            super.onDestroy();
            unregisterReceiver(minuteUpdate);
        }

    Запускаем - часы ходят. Готово!

    Здесь можно скачать .apk.

    Создание виджета-часы с кастомным шрифтом

    Будильник для Андроид "Разбуди меня"

    Программируем калькулятор на андроид. Урок 1.

    Андроид приложение - Списки.

    Клавиатура DNS KB-024BQ

    Игровая мышь A4Tech XL-750BH

    Многопользовательский режим работы в Excel

     

    Листинги MainActivity.java и WatchView.java

    MainActivity.java

    import java.util.Calendar;
    import java.util.Timer;
    import java.util.TimerTask;

    import android.app.Activity;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.graphics.PixelFormat;
    import android.os.Bundle;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.Display;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.RelativeLayout;

    public class MainActivity extends Activity {
        int screenH, screenW, sizeGage;
        WatchView watch;
        Calendar c;
        MinuteUpdate minuteUpdate;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Display display = getWindowManager().getDefaultDisplay();
            DisplayMetrics metricsB = new DisplayMetrics();
            display.getMetrics(metricsB);
            screenW = metricsB.widthPixels;
            screenH = metricsB.heightPixels;
            setContentView(R.layout.activity_main);
            sizeGage = screenW - 10;
            if(screenW > screenH) sizeGage = screenH - 10;
            watch = (WatchView) findViewById(R.id.wvWatch);
            RelativeLayout.LayoutParams wparam = (RelativeLayout.LayoutParams) watch.getLayoutParams();
            wparam.width = sizeGage;
            wparam.height = sizeGage;
            watch.setLayoutParams(wparam);
            watch.setSizes(sizeGage);
            minuteUpdate = new MinuteUpdate();
            registerReceiver(minuteUpdate, new IntentFilter(
                    "android.intent.action.TIME_TICK"));
        }
        @Override
        protected void onResume(){
            new Timer().schedule(new TimerTask(){

                @Override
                public void run() {
                    UpdateTime();
                }
            }, 500);
            super.onResume();
        }
        
        private void UpdateTime(){
            c = Calendar.getInstance();
            int iHour = c.get(Calendar.HOUR_OF_DAY);
            int iMin = c.get(Calendar.MINUTE);
            watch.setTime(iHour, iMin);
        }
           
        @Override
        protected void onDestroy(){
            super.onDestroy();
            unregisterReceiver(minuteUpdate);
        }

        
        class MinuteUpdate extends BroadcastReceiver{
            @Override
            public void onReceive(Context context, Intent intent) {
                UpdateTime();
            }
        }
    }

    WatchView.java

    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;

    public class WatchView extends SurfaceView {
        
        Paint paint, paint_fill, paint2_fill;
        Path dial, hour_arrow, minute_arrow;
        int size, center, offsetA, offsetB, lenA, lenB;
        Matrix matrixHour, matrixMinute;
        int angleH, angleM;
        SurfaceHolder surfaceHolder;

        public WatchView(Context context, AttributeSet attrs) {
            super(context, attrs);
            surfaceHolder = getHolder();
            paint  = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setColor(Color.RED);
            paint.setStrokeWidth(3);
            paint.setStyle(Paint.Style.STROKE);
            paint_fill  = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint_fill.setColor(Color.RED);
            paint_fill.setStrokeWidth(3);
            paint_fill.setStyle(Paint.Style.FILL_AND_STROKE);
            paint2_fill  = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint2_fill.setColor(Color.BLUE);
            paint2_fill.setStrokeWidth(3);
            paint2_fill.setStyle(Paint.Style.FILL_AND_STROKE);
            dial = new Path();
            minute_arrow = new Path();
            hour_arrow = new Path();
            matrixHour = new Matrix();
            matrixMinute = new Matrix();
        }
        
           public void setTime(int h, int m){
            angleM = m * 6;
            angleH = (int)(h*30 + m/2);
            
            matrixMinute.reset();
            matrixMinute.setRotate(angleM, center, center);
            
            matrixHour.reset();
            matrixHour.setRotate(angleH, center, center);
            
            dial.reset();
            dial.addCircle(center, center, size/2 -15, Path.Direction.CW);
            dial.moveTo(15, center);
            dial.lineTo(lenA*5, center);
            dial.moveTo(size - 15, center);
            dial.lineTo(size - lenA*5, center);
            dial.moveTo(center, 15);
            dial.lineTo(center, lenA*5);
            dial.moveTo(center, size - 15);
            dial.lineTo(center, size - lenA*5);
            dial.moveTo(center + offsetA, center - offsetB);
            dial.lineTo(center + offsetA - lenA, center - offsetB + lenB);
            dial.moveTo(center + offsetB, center - offsetA);
            dial.lineTo(center + offsetB - lenB, center - offsetA + lenA);
            dial.moveTo(center + offsetB, center + offsetA);
            dial.lineTo(center + offsetB - lenB, center + offsetA - lenA);
            dial.moveTo(center + offsetA, center + offsetB);
            dial.lineTo(center + offsetA - lenA, center + offsetB - lenB);
            dial.moveTo(center - offsetA, center + offsetB);
            dial.lineTo(center - offsetA + lenA, center + offsetB - lenB);
            dial.moveTo(center - offsetB, center + offsetA);
            dial.lineTo(center - offsetB + lenB, center + offsetA - lenA);
            dial.moveTo(center - offsetB, center - offsetA);
            dial.lineTo(center - offsetB + lenB, center - offsetA + lenA);
            dial.moveTo(center - offsetA, center - offsetB);
            dial.lineTo(center - offsetA + lenA, center - offsetB + lenB);
                    
            hour_arrow.reset();
            hour_arrow.moveTo(center, center);
            hour_arrow.lineTo(center - (int)(lenA*1.5), center - (int)(lenA*1.5));
            hour_arrow.lineTo(center , size/4);
            hour_arrow.lineTo(center + (int)(lenA*1.5), center - (int)(lenA*1.5));
            hour_arrow.close();
            minute_arrow.reset();
            minute_arrow.moveTo(center, center);
            minute_arrow.lineTo(center - (int)(lenA*1.5), center - (int)(lenA*1.5));
            minute_arrow.lineTo(center , size/6);
            minute_arrow.lineTo(center + (int)(lenA*1.5), center - (int)(lenA*1.5));
            minute_arrow.close();
            
            minute_arrow.transform(matrixMinute);
            hour_arrow.transform(matrixHour);
            Log.d("myLog", "ReDraw before");
            Canvas canvas = surfaceHolder.lockCanvas();
            Log.d("myLog", "ReDraw after");
            canvas.drawColor(Color.WHITE);
            canvas.drawPath(dial, paint);
            canvas.drawPath(hour_arrow, paint_fill);
            canvas.drawPath(minute_arrow, paint2_fill);
            surfaceHolder.unlockCanvasAndPost(canvas);
        }
        public void setSizes(int x){
            size = x;
            center = x/2;
            offsetA = (int) Math.rint((size/2 - 15) * Math.sin(Math.toRadians(30)) - 4);
            offsetB = (int) Math.rint((size/2 - 15) * Math.sin(Math.toRadians(60)) - 4);
            lenA = size/45;
            lenB = (int)(lenA * 1.7);
            
        }
    }

    Категория: Андроид разработка | Добавил: Olelucoye (10.02.2015)
    Просмотров: 2566
    | Теги: java, Android, кастомный view | Рейтинг: 0.0/0
    Всего комментариев: 0
    Добавлять комментарии могут только зарегистрированные пользователи.
    [ Регистрация | Вход ]
    Меню сайта
    Категории раздела
    Андроид разработка [23]
    libGDX [24]
    Мои андроид проекты [6]
    Excel [7]
    Железяки [5]
    Скрипты в блокноте [4]
    Разное [1]
    Форма входа
    Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0
    Яндекс Метрика
    Яндекс.Метрика