android 指引页面
2018-04-17 10:39:47 ahaitongxue 阅读数 100

转自

https://www.jianshu.com/p/f7de559b9752

2017-08-07 10:28:40 daojin505 阅读数 164

不管是MFC,还是linux,还是android,UI开发都是如下两大核心机制:

第一个是消息循环,第二个是界面组织结构。

围绕着这些,衍生出来的OpenGL,SurfaceView,SurfaceFinger等都是为这两大机制服务的。

 

打一个比方。

 

 

消息循环是UI中的发动机。

 

界面组织结构就是UI的设计结构。

 

而其他的东西,则是建立在这些基础之上的。

 

理解这两大块儿,那么android的UI基础就学得差不多了。这个时候可以结合一些例子,来做一些真正有意义的开发,例如UI特效啊。自定义动画啊。。。。也可以顺便把动画机制给理解吃透。

 

 

接下来就学一下Canvas,SurfaceFlinger,Matrix,来做一些特效。

如果想更深入地学习。那么学习一下OpenGL。

再想深入的话,学习一下JNI编程。

再深入的话,把java虚拟机给了解一下,也许对于提高程序效率帮助很大。

 

 

本篇就介绍一下消息队列:

 

android_os_MessageQueue.cpp

MessageQueue.java

Looper.cpp (frameworks\base\native\android) 2369 2011/12/12

Looper.java (frameworks\base\core\java\android\os) 8874 2011/12/12
Handler.java (frameworks\base\core\java\android\os) 23620 2011/12/12

Activity中的事件默认都是在UI线程中发生的。

这意味着Activity中的任何一个函数执行完之后,都要回到消息队列,这个节点。handleMessage结束之后,就会再次去消息队列查看消息。这跟windows上开发的消息队列的概念是一致的。

 

 

1.入队:

入队的时候,按照Message.when的大小进行排序。如果时间相同,那么按照入队的先后进行排序。

如果入队的时候,时间戳为0,那么就激活消息管道。否则不激活等超时。

 

MessageQueue.java

    final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        final boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

            msg.when = when;
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

 

2. 遍历

遍历的时候,按照队列头部的时间戳(为0,则立即调用,否则等待超时),进行poll函数调用。

 

  final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }

                // If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount == 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

 

 

 

3.。 下面看一下looper的loop函数

 

Looper.java (frameworks\base\core\java\android\os) 8874 2011/12/12

 public static void loop() {
        Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        MessageQueue queue = me.mQueue;
        
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        
        while (true) {
            Message msg = queue.next(); // might block
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }

                long wallStart = 0;
                long threadStart = 0;

                // This must be in a local variable, in case a UI event sets the logger
                Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                    wallStart = SystemClock.currentTimeMicro();
                    threadStart = SystemClock.currentThreadTimeMicro();
                }

                msg.target.dispatchMessage(msg);

                if (logging != null) {
                    long wallTime = SystemClock.currentTimeMicro() - wallStart;
                    long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;

                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                    if (logging instanceof Profiler) {
                        ((Profiler) logging).profile(msg, wallStart, wallTime,
                                threadStart, threadTime);
                    }
                }

                // Make sure that during the course of dispatching the
                // identity of the thread wasn't corrupted.
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {
                    Log.wtf(TAG, "Thread identity changed from 0x"
                            + Long.toHexString(ident) + " to 0x"
                            + Long.toHexString(newIdent) + " while dispatching to "
                            + msg.target.getClass().getName() + " "
                            + msg.callback + " what=" + msg.what);
                }
                
                msg.recycle();
            }
        }
    }

sendMessage调用的queMessage,这与windows很不相同。

 

5. 一个小小的设计,则是效率上N倍以上的提升。

另外在UI开发中invalidate到最后的时候,就是一个sendMessage,而不是直接调用traversal方法。为的就是异步处理,防止一个循环周期中调用多次invalidate。这样可以给程序一个联合rect的机会。

代码如下:

 

注意mTraversalScheduled这个成员,是理解本机制的关键:

ViewRoot.java

    void invalidate() {
        mDirty.set(0, 0, mWidth, mHeight);
        scheduleTraversals();
    }

 

 public void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;

            //noinspection ConstantConditions
            if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
                final long now = System.nanoTime();
                Log.d(TAG, "Latency: Scheduled traversal, it has been "
                        + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
                        + "ms since the last traversal finished.");
            }

            sendEmptyMessage(DO_TRAVERSAL);
        }
    }

 

完毕。这是我对UI开发的一些基础性的理解,请扔砖。也希望能抛砖引玉吧。

 

6.next函数一开始就调用了poolInner。来扫描一下用户事件,并直接调用到onTouchEvent等:

 

int Looper::pollInner(int timeoutMillis) {
#if DEBUG_POLL_AND_WAKE
    LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif

    // Adjust the timeout based on when the next message is due.
    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
        if (messageTimeoutMillis >= 0
                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
            timeoutMillis = messageTimeoutMillis;
        }
#if DEBUG_POLL_AND_WAKE
        LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
                this, mNextMessageUptime - now, timeoutMillis);
#endif
    }

    // Poll.
    int result = ALOOPER_POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;

#ifdef LOOPER_STATISTICS
    nsecs_t pollStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
#endif

#ifdef LOOPER_USES_EPOLL
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
#else
    // Wait for wakeAndLock() waiters to run then set mPolling to true.
    mLock.lock();
    while (mWaiters != 0) {
        mResume.wait(mLock);
    }
    mPolling = true;
    mLock.unlock();

    size_t requestedCount = mRequestedFds.size();
    int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
#endif

    // Acquire lock.
    mLock.lock();

    // Check for poll error.
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        LOGW("Poll failed with an unexpected error, errno=%d", errno);
        result = ALOOPER_POLL_ERROR;
        goto Done;
    }

    // Check for poll timeout.
    if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
        LOGD("%p ~ pollOnce - timeout", this);
#endif
        result = ALOOPER_POLL_TIMEOUT;
        goto Done;
    }

    // Handle all events.
#if DEBUG_POLL_AND_WAKE
    LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif

#ifdef LOOPER_USES_EPOLL
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeReadPipeFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
            }
        } else {
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
Done: ;
#else
    for (size_t i = 0; i < requestedCount; i++) {
        const struct pollfd& requestedFd = mRequestedFds.itemAt(i);

        short pollEvents = requestedFd.revents;
        if (pollEvents) {
            if (requestedFd.fd == mWakeReadPipeFd) {
                if (pollEvents & POLLIN) {
                    awoken();
                } else {
                    LOGW("Ignoring unexpected poll events 0x%x on wake read pipe.", pollEvents);
                }
            } else {
                int events = 0;
                if (pollEvents & POLLIN) events |= ALOOPER_EVENT_INPUT;
                if (pollEvents & POLLOUT) events |= ALOOPER_EVENT_OUTPUT;
                if (pollEvents & POLLERR) events |= ALOOPER_EVENT_ERROR;
                if (pollEvents & POLLHUP) events |= ALOOPER_EVENT_HANGUP;
                if (pollEvents & POLLNVAL) events |= ALOOPER_EVENT_INVALID;
                pushResponse(events, mRequests.itemAt(i));
            }
            if (--eventCount == 0) {
                break;
            }
        }
    }
Done:
    // Set mPolling to false and wake up the wakeAndLock() waiters.
    mPolling = false;
    if (mWaiters != 0) {
        mAwake.broadcast();
    }
#endif

#ifdef LOOPER_STATISTICS
    nsecs_t pollEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
    mSampledPolls += 1;
    if (timeoutMillis == 0) {
        mSampledZeroPollCount += 1;
        mSampledZeroPollLatencySum += pollEndTime - pollStartTime;
    } else if (timeoutMillis > 0 && result == ALOOPER_POLL_TIMEOUT) {
        mSampledTimeoutPollCount += 1;
        mSampledTimeoutPollLatencySum += pollEndTime - pollStartTime
                - milliseconds_to_nanoseconds(timeoutMillis);
    }
    if (mSampledPolls == SAMPLED_POLLS_TO_AGGREGATE) {
        LOGD("%p ~ poll latency statistics: %0.3fms zero timeout, %0.3fms non-zero timeout", this,
                0.000001f * float(mSampledZeroPollLatencySum) / mSampledZeroPollCount,
                0.000001f * float(mSampledTimeoutPollLatencySum) / mSampledTimeoutPollCount);
        mSampledPolls = 0;
        mSampledZeroPollCount = 0;
        mSampledZeroPollLatencySum = 0;
        mSampledTimeoutPollCount = 0;
        mSampledTimeoutPollLatencySum = 0;
    }
#endif

    // Invoke pending message callbacks.
    mNextMessageUptime = LLONG_MAX;
    while (mMessageEnvelopes.size() != 0) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
        if (messageEnvelope.uptime <= now) {
            // Remove the envelope from the list.
            // We keep a strong reference to the handler until the call to handleMessage
            // finishes.  Then we drop it so that the handler can be deleted *before*
            // we reacquire our lock.
            { // obtain handler
                sp<MessageHandler> handler = messageEnvelope.handler;
                Message message = messageEnvelope.message;
                mMessageEnvelopes.removeAt(0);
                mSendingMessage = true;
                mLock.unlock();

#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
                LOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
                        this, handler.get(), message.what);
#endif
                handler->handleMessage(message);
            } // release handler

            mLock.lock();
            mSendingMessage = false;
            result = ALOOPER_POLL_CALLBACK;
        } else {
            // The last message left at the head of the queue determines the next wakeup time.
            mNextMessageUptime = messageEnvelope.uptime;
            break;
        }
    }

    // Release lock.
    mLock.unlock();

    // Invoke all response callbacks.
    for (size_t i = 0; i < mResponses.size(); i++) {
        const Response& response = mResponses.itemAt(i);
        ALooper_callbackFunc callback = response.request.callback;
        if (callback) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
            LOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
                    this, callback, fd, events, data);
#endif
            int callbackResult = callback(fd, events, data);
            if (callbackResult == 0) {
                removeFd(fd);
            }
            result = ALOOPER_POLL_CALLBACK;
        }
    }
    return result;
}

 

 

2012-12-19 09:53:45 liaols_ 阅读数 808

ViewPager实现功能:

当滑动局部页面时,底部的圆点图片相应指引当前页面。

效果图:


android之ViewPager简单实现局部滑动效果之后,在原先的代码的基础上继续添加功能。

【main.xml】

在main.xml中添加水平方向并列排放的三个圆点图片ImageView,用LinearLayout组织排列布局。第一个圆点默认是选中状态

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="fill_parent"
        android:layout_height="200dp" />

    <LinearLayout
        android:id="@+id/iv_circle_dots"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:gravity="center_horizontal" >

        <ImageView
            android:id="@+id/iv_dot_01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/dot_selected" />

        <ImageView
            android:id="@+id/iv_dot_02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:src="@drawable/dot_unselected" />

        <ImageView
            android:id="@+id/iv_dot_03"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:src="@drawable/dot_unselected" />
    </LinearLayout>

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:text="实现viewPager局部滑动效果" />

</LinearLayout>

【TestViewPager01Activity】

原先代码基础上加入以下代码:

1>用一个类型为ImageView的数组来存放三个圆点

private ImageView mCircleDots[];

2>在initViews()方法中添加以下代码。数组大小为滑动页面的数量。

	    mCircleDots = new ImageView[mViewList.size()];//圆点数量和滑动的页面数量一致
	    mCircleDots[0] = (ImageView)findViewById(R.id.iv_dot_01);
	    mCircleDots[1] = (ImageView)findViewById(R.id.iv_dot_02);
	    mCircleDots[2] = (ImageView)findViewById(R.id.iv_dot_03);

3>在initViews()方法中添加监听

mViewPager.setOnPageChangeListener(new ViewPagerChangeListener());

4>监听页面改变时,圆点图片随之变化。

	class ViewPagerChangeListener implements OnPageChangeListener{

		@Override
		public void onPageScrollStateChanged(int arg0) {}

		@Override
		public void onPageScrolled(int arg0, float arg1, int arg2) {}

		//记录上一次滑到的是第几个页面
		int temp = 0;
		@Override
		public void onPageSelected(int arg0) {
			//arg0代表当前页面的index,若是第一个页面则arg0=0,第二个页面arg0=1以此类推
			//当我们滑动页面后,指引相应页面的圆点图片更换为选中状态的图片
			mCircleDots[arg0].setImageResource(R.drawable.dot_selected);
			if(temp < arg0){//从右向左滑
				mCircleDots[arg0-1].setImageResource(R.drawable.dot_unselected);
			}else if(temp>arg0){//从左向右滑
				mCircleDots[arg0+1].setImageResource(R.drawable.dot_unselected);
			}
			temp = arg0;
		}
	}

【完整代码】

package com.viewpager;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class TestViewPager01Activity extends Activity {

	private ViewPager mViewPager;
	private List<View> mViewList = new ArrayList<View>();//存放待滑动的view
	private LayoutInflater mInflater;
	private View view_01, view_02, view_03;//三个待滑动的view
	private ImageView mCircleDots[];
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initViews();  
    }
	private void initViews() {
		mInflater = getLayoutInflater();
		view_01 = mInflater.inflate(R.layout.layout1, null);
		view_02 = mInflater.inflate(R.layout.layout2, null);
		view_03 = mInflater.inflate(R.layout.layout3, null);
		
	    mViewList.add(view_01);
	    mViewList.add(view_02);
	    mViewList.add(view_03);
	    
	    mCircleDots = new ImageView[mViewList.size()];//圆点数量和滑动的页面数量一致
	    mCircleDots[0] = (ImageView)findViewById(R.id.iv_dot_01);
	    mCircleDots[1] = (ImageView)findViewById(R.id.iv_dot_02);
	    mCircleDots[2] = (ImageView)findViewById(R.id.iv_dot_03);

		mViewPager = (ViewPager)findViewById(R.id.viewPager);
		mViewPager.setAdapter(new MyPagerAdapter());
		mViewPager.setCurrentItem(0);//设置当前pager
		mViewPager.setOnPageChangeListener(new ViewPagerChangeListener());
	}
	
	
	class MyPagerAdapter extends PagerAdapter{
		
		@Override
		public int getCount() {//返回view数量
			return mViewList.size();
		}

		@Override
		public boolean isViewFromObject(View arg0, Object arg1) {
			return arg0 == arg1;
		}

		@Override
		public void destroyItem(ViewGroup container, int position, Object object) {
			container.removeView(mViewList.get(position));
		}

		@Override
		public Object instantiateItem(ViewGroup container, int position) {
			container.addView(mViewList.get(position), 0);
			return mViewList.get(position);
		}
	}
	
	class ViewPagerChangeListener implements OnPageChangeListener{

		@Override
		public void onPageScrollStateChanged(int arg0) {}

		@Override
		public void onPageScrolled(int arg0, float arg1, int arg2) {}

		//记录上一次滑到的是第几个页面
		int temp = 0;
		@Override
		public void onPageSelected(int arg0) {
			//arg0代表当前页面的index,若是第一个页面则arg0=0,第二个页面arg0=1以此类推
			//当我们滑动页面后,指引相应页面的圆点图片更换为选中状态的图片
			mCircleDots[arg0].setImageResource(R.drawable.dot_selected);
			if(temp < arg0){//从右向左滑
				mCircleDots[arg0-1].setImageResource(R.drawable.dot_unselected);
			}else if(temp>arg0){//从左向右滑
				mCircleDots[arg0+1].setImageResource(R.drawable.dot_unselected);
			}
			temp = arg0;
		}
	}
}









2016-05-11 15:44:26 Army_Jun 阅读数 391

一、精深

首先说一下精深,作为Android程序员,所必需掌握的基础知识:
1. Java核心编程
Android的开发语言是Java,所以Java是作为一名Android程序员所必须掌握的。
我这里推荐几本书,Think in Java(Java编程思想)、Core Java(Java核心技术)、Effective Java,看过这三本书,你的Java技术就相当有保障了。

2. Android基础知识
Android四大组件及生命周期
Layout布局方式
各种控件的使用方式
Activity间传值、Activity与Fragment间传值
不同分辨率的界面适配
事件及回调机制
本地数据存储
HTTP访问网络
Widget的使用
针对各个Android版本的适配

3. 进阶
adb命令的使用
Activity、Service、Broadcaster互相调用
通知栏Notification的使用
Alarm的使用
SMS的使用
线程的使用
Handler消息机制
AsyncTask异步调用机制
Intent、Intent-filter的使用
使用自定义style、theme、drawable等方式美化界面
动画效果的使用
硬件调用,摄像头、录音、录像、地理位置
APP版本升级、本地数据库版本升级
音频、视频处理
自定义布局、界面
WebView的使用以及WebView和本地代码间通讯
各种第三方类库的使用
各种第三方平台的使用

4. 高阶
2D/3D图形应用
传感器的使用
蓝牙、NFC等的使用
性能优化
NDK的使用
Framework层修改
自定义ROM
适配Android系统到不同的硬件设备

个人认为:
基础阶段可以制作出能用的功能简单的APP、这是一个普通开发人员所必须掌握的;
进阶阶段可以制作出好用的能吸引人的APP、这是一个技术负责人所必须掌握的;
高阶阶段则可以制作出MIUI这样的系统级别的框架了;更可以进军眼下火热的智能家居、智能眼镜行业了。这是一个技术总监所应具有的素质。

二、扩展

如果你要是想走广博这条路,那么会有很多新奇的、好玩的技术等着你。

但是首先,你要达到Android进阶水准,否则就不要自称是Android程序员了。
当你达到了进阶水准后,你的兴趣可能就不仅仅止于Android,可能你对iOS有兴趣,对服务端开发有兴趣,对Web开发有兴趣,对HTML5开发有兴趣,对设计有兴趣,对游戏开发有兴趣,等等等等,我这里对每一个方面都做一些简单介绍吧。

iOS:
iOS和Android就是一对儿双胞胎,很多人在做了一段时间的Android开发后,或者做了一段时间的iOS开发后,都想学学对方的技术。
个人认为iOS的学习曲线较Android要高,学习成本也较大,苹果三件套是必备的,不过iOS开发真的很火呀,而且开发出来的APP美观度比Android不知道高到哪里去了。

服务端:
Android APP是客户端,那么对于大多数的APP来说,是需要服务端提供服务、数据之类的,那么现今流行的服务端有哪些语言呢?
首推Java,Java的框架多呀,开发快呀,资料多呀,像SSH、Spring MVC、Jfinal、ofBiz呀,这些都是很流行的框架,我这里推荐Jfinal,典型的快速开发框架。
其他像Python的Django、Ruby on Rails、ThinkPhp等都是动态语言Web框架的经典,可以根据各自兴趣着重学习一种。
最不推荐的就是.Net,虽然VS很强大,WebService也很不错,但是个人认为不像Java那么规范,也不像动态语言那么快速,最不喜欢.Net框架,个人偏好而已。

前端:
我把前端技术定义为HTML(5)、CSS(3)、JS等这些技术的合并体,网页所最终呈现出来的效果都是由我们的前端工程师完成的。
而且现在有非常多的前端框架,像Bootstrap、JQuery UI、Semantic UI、NEJ、Pure UI、Amaze UI等等,都是现在很流行的框架,大家可以学习学习。

设计:
我认为的设计分为交互设计、视觉设计,
视觉设计肯定要掌握Photoshop之类的设计工具,并且有一定的美感(这个就仁者见仁了),
交互设计是现在设计中很需要强调的一部分,很多网站、APP外观看起来也不错,但是实际使用就感觉有些别扭,不尽如人意,那就是交互设计有缺陷。
一个经典问题:弹出框的[确定][取消]按钮,iOS默认确定在右,Android默认确定在左,那么如果你是一个交互设计师,你的APP需要同时在两个平台发布,这个按钮你应该如何设计呢?

游戏:
现在游戏也是多种多样,页游、端游、手游一波一波的,我这里只对手游有过了解,所以只说说这一部分。
手游现在国内最火的框架就是cocos2d了,支持多种平台,而且有很多的资源可以参考;
另外像Unity、OGEngine、AndEngine、LGame、也都是不错框架。

等你掌握了以上这些技术之后,你就是一个典型的全栈工程师了,从设计到开发到运营你都可以胜任,这个时候,你就可以跟老板说:我可以干五个人的活,只要三个人的工资了。
更可以在家Soho了,作为自由职业者,或者创业也都是不错的选择的。

选择一条路,走自己的路,让别人说去吧。

2019-02-21 23:33:28 u011382969 阅读数 9

第一部分 android UI

1.常用控件

约束布局:https://www.jianshu.com/p/17ec9bd6ca8a

2、项目中常见界面基本技术实现

http://www.cnblogs.com/jerehedu/p/4607599.html#dxjmsj

 

android的UI开发工程师指引

阅读数 11

不管是MFC,还是linux,还是android,UI开发都是如下两大核心机制:第一个是消息循环,第二个是界面组织结构。围绕着这些,衍生出来的OpenGL,SurfaceView,SurfaceFinger等都是为这两大机制服务的。 打一个比方。  消息循环是UI中的发动机。 界面组织结构就是UI的设计结构。 而其他的东西,则是建立在这些基础之上的。...

博文 来自: daojin505

uexGaodeMap插件Android接入指引

阅读数 958

uexGaodeMap插件Android接入指引高德地图插件是基于高德地图API封装的AppCan平台的插件模块。开发者集成及使用此插件,需要到高德开放平台为应用申请相应的APIKey,并将APIKey配置到应用中。以下是具体步骤。高德地图开放平台创建应用申请成为开发者,访问http://lbs.amap.com/。注册成功之后,点击成为开发者,如下:填写完整具体的信息申请成功之后

博文 来自: WatermelonZero

Android Studio新手完全指引

阅读数 294

AndroidStudio的下载及安装如果你的电脑可以翻墙,那么请直接到Android官网下载,如下图所示:如果不幸翻不了墙,没关系,可以到AndroidDevTools下载,这个网站基本涵盖了Android开发所需的所有工具,文件放在百度云上面,下载速度杠杠的。下载并安装完成后,首次启动会去下载更新一系列文件,如果已经翻了墙,就等待其更新完成;如果没有翻墙,

博文 来自: BianHuanShiZhe

Android Studio新手指引

阅读数 229

AndroidStudio的下载及安装如果你的电脑可以翻墙,那么请直接到Android官网下载,如下图所示:如果不幸翻不了墙,没关系,可以到AndroidDevTools下载,这个网站基本涵盖了Android开发所需的所有工具,文件放在百度云上面,下载速度杠杠的。下载并安装完成后,首次启动会去下载更新一系列文件,如果已经翻了墙,就等待其更新完成;如果没有翻墙,

博文 来自: u013598111

android 滑动指引页的设计

阅读数 4

导言设计补充导言一个程序第一次安装我们都会用几个引导页告诉用户这个程序是怎么做的,这个项目就是基于viewflow项目利用android新apifragment系统设计的一个公共类库设计  整个应用的流程就是如上图所示,因为我用到了viewflow的库,所以,整个过程只是用fragment做了一层封装而已,如果你想自定义的话,...

博文 来自: weixin_33862041
没有更多推荐了,返回首页