flutter_测试网络

import 'dart:convert';

// import 'package:dio/dio.dart';
// import 'package:pupilmath_web/datamodel/exercise_entity_list.dart';
// import 'package:pupilmath_web/datamodel/exercise_result_data_model.dart';
// import 'package:pupilmath_web/datamodel/push_exercise_response.dart';
// import 'package:pupilmath_web/entity_factory.dart';
// import 'package:pupilmath_web/my/httprequest.dart';
// import 'package:pupilmath_web/network/constant.dart';
// import 'package:pupilmath_web/network/network.dart';
// import 'package:pupilmath_web/viewmodel/exercise/viewmodel_base_multi_exercise.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;


var responseBody;   

void main()async{
  // String _url = '$exerciseUrl/$exerciseId/questions';

    String testUrl = "http://pupilsandbox.inner.youdao.com/ypm-web/exercise/C15B1EBE688C461BA2BE60453139ED5A/questions";

    var params = Map<String, String>();
    params["levelNum"] = "2";

    var body = json.encode({"levelNum": "2"});

    var client = http.Client();  
    var response = await client.post(testUrl,headers: {
      "content-type" : "application/json",
    }, body: body).then((response){
      responseBody = response.body;  
      print(response.body);  
    });

//    _content = response.body;
    // printHelper("response::" + response.body.toString());
    runApp(MyApp());
}


class MyApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("hello"),),
        body: Text(responseBody!=null?responseBody:"空"),)
    );  
  }
}
发表在 未分类 | 留下评论

flutter_cupertino_date_picker

import 'package:chewie/chewie.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:flutter_cupertino_date_picker/flutter_cupertino_date_picker.dart';
final videoPlayerController = VideoPlayerController.asset("assets/test.mp4");  


final chewieController = ChewieController(
  videoPlayerController: videoPlayerController,
  aspectRatio: 3 / 2,
  autoPlay: true,
  looping: true,
);

final playerWidget = Chewie(
  controller: chewieController,
);


void main(){
  runApp(MyApp());  
}

class MyApp extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return StateMyApp();  
  }

}

class StateMyApp extends State<MyApp>{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("名字"),),
        body: Center(
          child: DatePickerWidget(

            // DateTime minDateTime,
            // DateTime maxDateTime,
            // DateTime initialDateTime,
            // String dateFormat: DATETIME_PICKER_DATE_FORMAT,
            // DateTimePickerLocale locale: DATETIME_PICKER_LOCALE_DEFAULT,
            // DateTimePickerTheme pickerTheme: DatePickerTheme.Default,
            // DateVoidCallback onCancel,
            // DateValueCallback onChange,
            // DateValueCallback onConfirm,
)),
      ),
    );
  }
}
发表在 未分类 | 留下评论

Flutter_AppBar

AppBar的使用:

import 'package:flutter/material.dart';

class BasicAppBarSample extends StatefulWidget {
  @override
  _BasicAppBarSampleState createState() => new _BasicAppBarSampleState();
}

class _BasicAppBarSampleState extends State<BasicAppBarSample> {
  Choice _selectedChoice = choices[0]; // The app's "state".

  //选中这个Choice进行展示
  void _select(Choice choice) {
    setState(() {
      // Causes the app to rebuild with the new _selectedChoice.
      _selectedChoice = choice;
    });
  }

  @override
  Widget build(BuildContext context) {
    //返回的还是MaterialApp
    return new MaterialApp(
      //Scafflold意思是架子的意思
      home: new Scaffold(
        appBar: new AppBar(
          //app中的标题
          title: const Text('Basic AppBar'),
          //可以响应的动作
          actions: <Widget>[
            //图标按钮
            new IconButton(
              icon: new Icon(choices[0].icon),
              onPressed: () {
                _select(choices[0]);
              },
            ),
            new IconButton(
              // action button
              icon: new Icon(choices[1].icon),
              onPressed: () {
                _select(choices[1]);
              },
            ),
            //其他的放入一个弹出吧按钮来进行
            new PopupMenuButton<Choice>(
              // overflow menu
              //点击所执行的方法
              onSelected: _select,

              //创建Item的方法
              itemBuilder: (BuildContext context) {
                //跳过两个
                return choices.skip(2).map((Choice choice) {
                  ///将每个都映射为弹出菜单的一个按钮
                  return new PopupMenuItem<Choice>(
                    //对应的值是choice
                    value: choice,
                    child: new Text(choice.title),
                  );
                }).toList();
              },
            ),
          ],
        ),
        body: new Padding(
          ///内边距
          padding: const EdgeInsets.all(16.0),

          child: new ChoiceCard(choice: _selectedChoice),
        ),
      ),
    );
  }
}

///包含对应icon和文本的类
class Choice {
  const Choice({this.title, this.icon});

  final String title;
  final IconData icon;
}

///Choice的集合,是一个常量
const List<Choice> choices = const <Choice>[
  const Choice(title: 'Car', icon: Icons.directions_car),
  const Choice(title: 'Bicycle', icon: Icons.directions_bike),
  const Choice(title: 'Boat', icon: Icons.directions_boat),
  const Choice(title: 'Bus', icon: Icons.directions_bus),
  const Choice(title: 'Train', icon: Icons.directions_railway),
  const Choice(title: 'Walk', icon: Icons.directions_walk),
];


///显示的图片
class ChoiceCard extends StatelessWidget {
  ///参数是可选的
  const ChoiceCard({Key key, this.choice}) : super(key: key);

  final Choice choice;

  @override
  Widget build(BuildContext context) {
    final TextStyle textStyle = Theme.of(context).textTheme.display1;
    return new Card(
      ///白色的
      color: Colors.white,
      ///
      child: new Center(
        child: new Column(
          ///主轴尺寸:
          mainAxisSize: MainAxisSize.min,
          ///横轴对齐方式:中心对齐
          crossAxisAlignment: CrossAxisAlignment.center,
          ///
          children: <Widget>[
            new Icon(choice.icon, size: 128.0, color: textStyle.color),
            new Text(choice.title, style: textStyle),
          ],
        ),
      ),
    );
  }
}

void main() {
  runApp(new BasicAppBarSample());
}

发表在 flutter | 留下评论

Handler内存泄漏

Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference)

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;
/**
 * Created by zejian on 16/3/6.
 */
public class HandlerActivity extends Activity {
    //创建一个2M大小的int数组
    int[] datas=new int[1024*1024*2];
//    Handler mHandler = new Handler(){
//        @Override
//        public void handleMessage(Message msg) {
//            super.handleMessage(msg);
//        }
//    };
    /**
     * 创建静态内部类
     */
    private static class MyHandler extends Handler{
        //持有弱引用HandlerActivity,GC回收时会被回收掉.
        private final WeakReference<HandlerActivity> mActivty;
        public MyHandler(HandlerActivity activity){
            mActivty =new WeakReference<HandlerActivity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            HandlerActivity activity=mActivty.get();
            super.handleMessage(msg);
            if(activity!=null){
                //执行业务逻辑
            }
        }
    }
    private static final Runnable myRunnable = new Runnable() {
        @Override
        public void run() {
            //执行我们的业务逻辑
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_leak);
        MyHandler myHandler=new MyHandler(this);
        //解决了内存泄漏,延迟5分钟后发送
        myHandler.postDelayed(myRunnable, 1000 * 60 * 5);
    }
}

也可以在onDestroy中将Handler置为空来处理。

发表在 Android | 留下评论

控件带点击波纹的LinearLayout

/**
 * 一个特殊的LinearLayout,任何放入内部的clickable元素都具有波纹效果,当它被点击的时候,
 * 为了性能,尽量不要在内部放入复杂的元素
 * note: long click listener is not supported current for fix compatible bug.
 * @author xuanqis
 */
public class RevealLayout extends LinearLayout implements Runnable {

    private static final String TAG = "DxRevealLayout";
    private static final boolean DEBUG = true;

    /**
     * 画笔
     */

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    /**
     * 点击的View的宽高
     */
    private int mTargetWidth;
    private int mTargetHeight;

    /**
     * 点击的View的宽与高的最小值和最大值
     */
    private int mMinBetweenWidthAndHeight;
    private int mMaxBetweenWidthAndHeight;



    /**
     * 点击的中心点所在的坐标
     */
    private float mCenterX;
    private float mCenterY;


    /**
     * 半径最大值、半径单位变化值、当前的半径、父view的左上角的坐标数组
     */
    private int mMaxRevealRadius;
    private int mRevealRadiusGap;
    private int mRevealRadius = 0;
    private int[] mLocationInScreen = new int[2];

    private boolean mShouldDoAnimation = false;
    private boolean mIsPressed = false;
    private int INVALIDATE_DURATION = 40;



    private View mTouchTarget;
    private DispatchUpTouchEventRunnable mDispatchUpTouchEventRunnable =
            new DispatchUpTouchEventRunnable();

    public RevealLayout(Context context) {
        super(context);
        init();
    }

    public RevealLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public RevealLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    /**
     * 进行初始化工作,设置画笔的颜色
     */
    private void init() {
        //有些ViewGroup设置了不能绘制,要将其进行重置
        setWillNotDraw(false);
        //设置画笔的颜色
        mPaint.setColor(getResources().getColor(R.color.reveal_color));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        this.getLocationOnScreen(mLocationInScreen);
    }

    /**
     * 根据点击的位置,确定波纹的最大半径,还有其他的初始半径等也初始化
     * @param event 点击事件,需要事件的坐标
     * @param view 进行初始化的View
     */
    private void initParametersForChild(MotionEvent event, View view) {

        //这里是相对于点击View的相对坐标,这个不知道为啥好像是相对于RevealLayout的坐标
        mCenterX = event.getX();
        mCenterY = event.getY();

        mTargetWidth = view.getMeasuredWidth();
        mTargetHeight = view.getMeasuredHeight();

        mMinBetweenWidthAndHeight = Math.min(mTargetWidth, mTargetHeight);
        mMaxBetweenWidthAndHeight = Math.max(mTargetWidth, mTargetHeight);

        //初始半径为0
        mRevealRadius = 0;
        //。。。
        mShouldDoAnimation = true;
        //。。。
        mIsPressed = true;
        //增加的单位是宽度和高度最小值的 1/8
        mRevealRadiusGap = mMinBetweenWidthAndHeight / 8;


        //这里以后看到了再看一遍,感觉是有点问题,不对劲

        int[] location = new int[2];
        view.getLocationOnScreen(location);
        // left:点击的View和父View的x坐标的差值
        int left = location[0] - mLocationInScreen[0];
        //transformedCenterX: 点击的点相对于View的偏移 - view相对于父View的偏移
        int transformedCenterX = (int) mCenterX - left;
        //最长的半径的值为
        mMaxRevealRadius = Math.max(transformedCenterX, mTargetWidth - transformedCenterX);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        //播放为为false、目标View的宽度为0、目标View为null
        if (!mShouldDoAnimation || mTargetWidth <= 0 || mTouchTarget == null) {
            return;
        }

        //增大半径,有点牛逼,一次到位
        if (mRevealRadius > mMinBetweenWidthAndHeight / 2) {
            mRevealRadius += mRevealRadiusGap * 4;
        } else {
            mRevealRadius += mRevealRadiusGap;
        }

        this.getLocationOnScreen(mLocationInScreen);
        int[] location = new int[2];
        mTouchTarget.getLocationOnScreen(location);
        //画布的显示范围
        int left = location[0] - mLocationInScreen[0];
        int top = location[1] - mLocationInScreen[1];
        int right = left + mTouchTarget.getMeasuredWidth();
        int bottom = top + mTouchTarget.getMeasuredHeight();

        //保存现有状态
        canvas.save();
        //设置画布的显示范围
        canvas.clipRect(left, top, right, bottom);
        //绘制波纹
        canvas.drawCircle(mCenterX, mCenterY, mRevealRadius, mPaint);
        //restore和save要配对使用
        canvas.restore();

        if (mRevealRadius <= mMaxRevealRadius) {
            //在指定的时间后,将指定的矩形进行刷新
            postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);
        } else if (!mIsPressed) {
            //不需要继续进行绘制了
            mShouldDoAnimation = false;
            postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        //得到相对于屏幕的绝对地址
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();

        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            //得到点击事件所在的具体的View
            View touchTarget = getTouchTarget(this, x, y);

            if (touchTarget != null && touchTarget.isClickable() && touchTarget.isEnabled()) {
                mTouchTarget = touchTarget;
                //使用事件去初始化View的相关信息
                initParametersForChild(event, touchTarget);
                //等40ms进行重绘制
                postInvalidateDelayed(INVALIDATE_DURATION);
            }
        } else if (action == MotionEvent.ACTION_UP) {
            mIsPressed = false;
            //抬起进行重绘,颜色消失
            postInvalidateDelayed(INVALIDATE_DURATION);

            mDispatchUpTouchEventRunnable.event = event;
            postDelayed(mDispatchUpTouchEventRunnable, 200);
            return true;
        } else if (action == MotionEvent.ACTION_CANCEL) {
            mIsPressed = false;
            postInvalidateDelayed(INVALIDATE_DURATION);
        }

        return super.dispatchTouchEvent(event);
    }

    /**
     * 得到ViewGroup的点击的点在哪个孩子中
     * @param view
     * @param x
     * @param y
     * @return 点所在的孩子
     */
    private View getTouchTarget(View view, int x, int y) {
        View target = null;
        ArrayList<View> TouchableViews = view.getTouchables();
        for (View child : TouchableViews) {
            if (isTouchPointInView(child, x, y)) {
                target = child;
                break;
            }
        }

        return target;
    }

    /**
     * 判断点击的点(x,y)是否在View内
     * @param view 判断的View
     * @param x x坐标
     * @param y y坐标
     * @return 点是否在View内
     */
    private boolean isTouchPointInView(View view, int x, int y) {
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int left = location[0];
        int top = location[1];
        int right = left + view.getMeasuredWidth();
        int bottom = top + view.getMeasuredHeight();
        return view.isClickable() && y >= top && y <= bottom
                && x >= left && x <= right;
    }

    @Override
    public boolean performClick() {
        postDelayed(this, 400);
        return true;
    }

    @Override
    public void run() {
        super.performClick();
    }

    private class DispatchUpTouchEventRunnable implements Runnable {
        public MotionEvent event;

        @Override
        public void run() {
            if (mTouchTarget == null || !mTouchTarget.isEnabled()) {
                return;
            }

            if (isTouchPointInView(mTouchTarget, (int) event.getRawX(), (int) event.getRawY())) {
                mTouchTarget.performClick();
            }
        }
    }

}
发表在 未分类 | 留下评论

外部拦截的横向LinearLayout的特例

/**
 * 横向的LinearLayout
 */
public class HorizontalScrollViewEx extends ViewGroup {
    private static final String TAG = "HorizontalScrollViewEx";

    private int mChildrenSize;
    private int mChildWidth;
    private int mChildIndex;

    /**
     * 分别记录上次滑动的坐标
     * 上一个事件的x和y坐标
     */

    private int mLastX = 0;
    private int mLastY = 0;

    /**
     * 分别记录上次滑动的坐标(onInterceptTouchEvent)
     */
    private int mLastXIntercept = 0;
    private int mLastYIntercept = 0;

    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;

    public HorizontalScrollViewEx(Context context) {
        super(context);
        init();
    }

    public HorizontalScrollViewEx(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public HorizontalScrollViewEx(Context context, AttributeSet attrs,int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * 初始化工作,创建滑动器和速度跟踪器
     */
    private void init() {
        //创建一个滑动器
        mScroller = new Scroller(getContext());
        //创建一个速度跟踪器
        mVelocityTracker = VelocityTracker.obtain();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                intercepted = false;
                //如果是外部正在滚动,那么
                //1. 横向,综合两次的进行滑动
                //2. 纵向滑动,不处理
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                    intercepted = true;
                }
                break;
            }
            //判断自己拦不拦截,拦截了得话,那么之后的事件也由当前处理
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastXIntercept;
                int deltaY = y - mLastYIntercept;
                intercepted = Math.abs(deltaX) > Math.abs(deltaY);
                break;
            }
            //不拦截松开动作
            case MotionEvent.ACTION_UP: {
                intercepted = false;
                break;
            }
            default:
                break;
        }

        Log.d(TAG, "intercepted=" + intercepted);
        mLastX = x;
        mLastY = y;
        mLastXIntercept = x;
        mLastYIntercept = y;

        return intercepted;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //添加事件,进行监听
        mVelocityTracker.addMovement(event);

        //得到x和y坐标
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                if (!mScroller.isFinished()) {
                    Log.d(TAG, "关闭");
                    mScroller.abortAnimation();
                }
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                //滑多远就跟着走多远
                scrollBy(-deltaX, 0);
                break;
            }
            //离开屏幕,进行页面的滚动计算
            case MotionEvent.ACTION_UP: {
                //返回离原位置滑动了多远
                int scrollX = getScrollX();
                int scrollToChildIndex = scrollX / mChildWidth;
                //以秒计算速度
                mVelocityTracker.computeCurrentVelocity(1000);
                //得到速度
                float xVelocity = mVelocityTracker.getXVelocity();
                if (Math.abs(xVelocity) >= 50) {
                    mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;
                } else {
                    //这里好像有点蠢
                    mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;
                }
                mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
                int dx = mChildIndex * mChildWidth - scrollX;
                smoothScrollBy(dx, 0);
                mVelocityTracker.clear();
                break;
            }
            default:
                break;
        }

        mLastX = x;
        mLastY = y;
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredWidth;
        int measuredHeight;
        //得到子控件的个数
        final int childCount = getChildCount();
        //先调用子控件的测量方法
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        //得到当前控件的宽度和高度的测量模式和测量大小
        int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

        //没有孩子,当前的View Group宽度和高度由孩子决定,所以也就是0
        if (childCount == 0) {
            setMeasuredDimension(0, 0);
        }
        //
        else if (heightSpecMode == MeasureSpec.AT_MOST && widthSpecMode==MeasureSpec.AT_MOST){
            final View childView = getChildAt(0);
            measuredWidth = childView.getMeasuredWidth() * childCount;
            measuredHeight = childView.getMeasuredHeight();
            setMeasuredDimension(measuredWidth, measuredHeight);
        }
        else if (heightSpecMode == MeasureSpec.AT_MOST) {
            final View childView = getChildAt(0);
            measuredHeight = childView.getMeasuredHeight();
            setMeasuredDimension(widthSpaceSize, measuredHeight);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            final View childView = getChildAt(0);
            measuredWidth = childView.getMeasuredWidth() * childCount;
            setMeasuredDimension(measuredWidth, heightSpaceSize);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        /**
         * 布局的工作其实也就把对应的子元素放到对应的位置就可以了
         * 其他倒是没啥了
         */
        int childLeft = 0;
        final int childCount = getChildCount();
        mChildrenSize = childCount;

        for (int i = 0; i < childCount; i++) {
            final View childView = getChildAt(i);
            if (childView.getVisibility() != View.GONE) {
                final int childWidth = childView.getMeasuredWidth();
                mChildWidth = childWidth;
                childView.layout(childLeft, 0, childLeft + childWidth,
                        childView.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }

    /**
     * 弹性的进行500ms滑动x方向dx, y方向dy
     * @param dx
     * @param dy
     */
    private void smoothScrollBy(int dx, int dy) {
        mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
        invalidate();
    }

    @Override
    public void computeScroll() {
        //配合smoothScrollBy方法来进行弹性滑动
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        //控件从window上面删除,速度跟踪回收
        mVelocityTracker.recycle();
        super.onDetachedFromWindow();
    }

}
发表在 未分类 | 留下评论

内部拦截的ListView

/**
 * 内部拦截的ListView
 */
public class ListViewEx extends ListView {
    private static final String TAG = "ListViewEx";


    /**
     * 分别记录上次滑动的坐标
     */
    private int mLastX = 0;
    private int mLastY = 0;

    public ListViewEx(Context context) {
        super(context);
    }

    public ListViewEx(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ListViewEx(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setHorizontalScrollViewEx2(
            HorizontalScrollViewEx2 horizontalScrollViewEx2) {
//        mHorizontalScrollViewEx2 = horizontalScrollViewEx2;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            //把父ViewGroup设为不拦截
            getParent().requestDisallowInterceptTouchEvent(true);
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int deltaX = x - mLastX;
            int deltaY = y - mLastY;
            Log.d(TAG, "dx:" + deltaX + " dy:" + deltaY);
            //父控件进行拦截,注意这里的getParent得到的不一定是个View
            if (Math.abs(deltaX) > Math.abs(deltaY)) {
                getParent().requestDisallowInterceptTouchEvent(false);
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
            break;
        }
        default:
            break;
        }

        mLastX = x;
        mLastY = y;
        return super.dispatchTouchEvent(event);
    }

}

发表在 未分类 | 留下评论

可以拖动的TextView

/**
 * 可以拖动的TextView,中间尝试使用scrollTo,没成功
 * 因为是继承的TextView,所以wrap_content的问题解决
 * Action_down和Action_up交给父类处理
 * @author xuanqis
 */
public class TestButton extends android.support.v7.widget.AppCompatTextView {
    private static final String TAG = "TestButton";
    /**
     * 能够认为发生了滑动的最短距离,单位是像素
     */
    private int mScaledTouchSlop;
    /**
     * 分别记录上次滑动的坐标
     */
    private int mLastX = 0;
    private int mLastY = 0;

    public TestButton(Context context) {
        this(context, null);
    }

    public TestButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TestButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * 得到发生滑动的最小距离
     */
    private void init() {
        //得到系统的滑动距离
        mScaledTouchSlop = ViewConfiguration.get(getContext())
                .getScaledTouchSlop();
        Log.d(TAG, "sts:" + mScaledTouchSlop);
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //得到的是相对于屏幕左上角的坐标
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                super.onTouchEvent(event);  
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                //偏移量=现在-原来
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                Log.d(TAG, "move, deltaX:" + deltaX + " deltaY:" + deltaY);
                //偏移量进行相加,进行移动
                int translationX = (int) ViewHelper.getTranslationX(this) + deltaX;
                int translationY = (int) ViewHelper.getTranslationY(this) + deltaY;
                ViewHelper.setTranslationX(this, translationX);
                ViewHelper.setTranslationY(this, translationY);
                break;
            }
            case MotionEvent.ACTION_UP: {
                super.onTouchEvent(event);
                break;
            }
            default:
                break;
        }

        mLastX = x;
        mLastY = y;
        return true;
    }
}


发表在 未分类 | 留下评论

binder连接池

  1. aidl文件
// IBinderPool.aidl
package com.example.a2.bindpool;


interface IBinderPool {

    IBinder queryBinder(int binderCode);
}

// ICompute.aidl
package com.example.a2.bindpool;

interface ICompute {
    int add(int a, int b);
}

// ISecurityCenter.aidl
package com.example.a2.bindpool;

//用来进行加密的
interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String password);
}
  1. Binder的实现
public class ComputImpl extends ICompute.Stub {
    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }
}

public class SecurityCenterImpl extends ISecurityCenter.Stub {

    private static final char SECRET_CODE='^';
    @Override
    public String encrypt(String content) throws RemoteException {
        char[] chars =content.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            chars[i] ^= SECRET_CODE;
        }
        return new String(chars);
    }

    @Override
    public String decrypt(String password) throws RemoteException {
        return encrypt(password);
    }
}

  1. Bindpool的封装类
/**
 * @author xuanqis
 * 单例模式,设为volatile,防止指令重排序
 * 具有服务端BinderPoolImpl的代理
 *
 */
public class BinderPool {
    private static final String TAG = "BinderPool";
    public static final int BIND_NONE = -1;
    public static final int BIND_COMPUT = 0;
    public static final int BIND_SECURITY_CENTER = 1;

    /**
     * 客户端的ApplicationContext
     */
    private Context mContext;

    /**
     * 客户端的代理BinderPool的Proxy,封装在BinderPool类里面
     */
    private IBinderPool mBinderPool;
    /**
     * 单例,懒加载模式的加载,这里设置为volatile防止指令重排序
     */
    private static volatile BinderPool sInstance;
    /**
     * 用来控制同步,回调onServiceConnected是异步的
     */
    private CountDownLatch mconnectBinderPoolCountDownLatch;

    /**
     * 对于每个进程,有唯一的一个服务端BinderPoolImpl的代理,注意这里单例的多线程处理
     * @param context 活动的上下文,如果当前进程没有Binder池的代理的话,就利用这个上下文进行创建
     * @return BinderPool类的单例
     */
    public static BinderPool getInstance(Context context){
        //处理多线程的问题,首先判断是不是null,是的话就获取锁,再判断是不是null,再创建单例
        if (sInstance==null){
            synchronized (BinderPool.class){
                if (sInstance==null){
                    sInstance=new BinderPool(context);
                }
            }
        }
        return sInstance;
    }

    /**
     * 一个活动创建Binder池就会直接将活动所在的Application与服务连接起来
     * @param context 活动上下文
     */
    private BinderPool(Context context){
        mContext=context.getApplicationContext();
        connectBinderPoolService();
    }

    /**
     * 将活动的应用和服务进行绑定,执行之后就有BinderPool的代理了,客户端
     */
    private synchronized void connectBinderPoolService(){
        mconnectBinderPoolCountDownLatch = new CountDownLatch(1);
        Intent intent = new Intent(mContext, BinderPoolService.class);
        mContext.bindService(intent, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
        try{
            //等待绑定完成,才执行结束
            mconnectBinderPoolCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     *BinderPool的实现类,运行在服务端,静态内部类,只会有一个
     */
    public static class BinderPoolImpl extends IBinderPool.Stub{
        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            Binder binder = null;
            switch (binderCode) {
                case BIND_SECURITY_CENTER:
                    binder=new SecurityCenterImpl();
                    break;
                case BIND_COMPUT:
                    binder=new ComputImpl();
                    break;
                default:
                    break;
            }
            return binder;
        }
    }

    /**
     * 连接类,其实是将服务器的Binder封装到了客户端的BinderPool里面
     */
    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mDeathRecipient, 0);

            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mconnectBinderPoolCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    /**
     * 失败重连,运行在线程池里面
     */
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG, "bind died...");
            mBinderPool.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mBinderPool=null;
            connectBinderPoolService();
        }
    };

    public IBinder queryBinder(int binderCode){
        IBinder binder = null;
        try{
            if (mBinderPool != null){
                binder=mBinderPool.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }

}
  1. Service服务,返回连接池Binder
public class BinderPoolService extends Service {
    private static final String TAG = "BinderPoolService";
    private Binder mBinderPool = new BinderPool.BinderPoolImpl();

    @Override
    public IBinder onBind(Intent intent) {
        //返回binder连接池的binder
        return mBinderPool;
    }
}
  1. 测试连接池的活动
public class BindpoolActivity extends AppCompatActivity {
    private static final String TAG ="BinderPoolActivity";
    /**
     * Binder连接池中的Binder代理
     */
    private ISecurityCenter mSecurityCenter;
    private ICompute mCompute;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bindpool);
        new Thread(new Runnable() {
            @Override
            public void run() {
                doWork();
            }
        }).start();
    }

    private void doWork(){
        //得到当前进程的Binder连接池
        BinderPool binderPool=BinderPool.getInstance(this);

        IBinder securityBinder = binderPool.queryBinder(BinderPool.BIND_SECURITY_CENTER);
        mSecurityCenter=SecurityCenterImpl.asInterface(securityBinder);
        Log.d(TAG, "visit IsecurityBinder");
        String msg = "hello android...";
        Log.d(TAG, "content:"+msg);
        try{
            String password = mSecurityCenter.encrypt(msg);
            Log.d(TAG, "加密:"+password);
            Log.d(TAG, "解密:"+mSecurityCenter.decrypt(password));
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        Log.d(TAG, "visit icompute...");
        IBinder computeBinder = binderPool.queryBinder(BinderPool.BIND_COMPUT);
        mCompute=ComputImpl.asInterface(computeBinder);
        try{
            Log.d(TAG, "3+5="+mCompute.add(3,5));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}
发表在 Android | 留下评论

TCP通信

  1. 注意不能在主线程中进行网络操作,不能在子线程更新UI。

  2. 活动的布局

<?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:background="#ffffff"
    android:orientation="vertical"
    android:padding="5dp" >

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/msg_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </ScrollView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <EditText
            android:id="@+id/msg"
            android:layout_width="0dp"
            android:layout_height="35dp"
            android:layout_weight="1"
            android:background="@drawable/edit"
            android:ems="10"
            android:padding="5dp" />

        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            android:enabled="false"
            android:text="发送" />
    </LinearLayout>

</LinearLayout>
  1. 服务端
public class TCPServerService extends Service {
    private static final String TAG = "TCPServerService";
    /**
     * 启动的线程已经脱离服务的主线程了,
     */
    private boolean mIsServerDestroyed = false;
    private String[] mDefinedMessages = new String[]{
            "你好呀,哈哈",
            "请问你叫什么名字",
            "今天的天气不错",
            "你知道吗,我可以和多个人聊天哦",
            "给你讲个笑话吧,据说爱笑的人运气不会太差,不知道真假"
    };

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
        Log.d(TAG, "onCreate");
        new Thread(new TcpServer()).start();
    }

    public TCPServerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        mIsServerDestroyed=true;
        super.onDestroy();
    }

    private class TcpServer implements Runnable{
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try{
                //建立服务器的端口
                serverSocket=new ServerSocket(8888);
            } catch (IOException e) {
                e.printStackTrace();
                Log.d(TAG, "establish tcp server failed, port:8888");
                return ;
            }
            if (serverSocket!=null){
                Log.d(TAG, "服务器端口成功...");
            }
            //如果没有服务没有被杀死
            while(!mIsServerDestroyed){
                try{
                    //得到和客户端的连接端口
                    final Socket client = serverSocket.accept();
                    Log.d(TAG, "accept socket");
                    new Thread(){
                        @Override
                        public void run() {
                            try{
                                //开个新的线程,在这个新的线程里面进行和客户的交流
                                responseClient(client);
                            }catch (IOException e){
                                e.printStackTrace();
                            }
                        }
                    }.start();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.d(TAG, "连接失败");
                }
            }
        }
    }

    /**
     * 进行和客户端的交流
     * @param client 和客户端连接的端口
     * @throws IOException
     */
    private void responseClient(Socket client) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        PrintWriter out = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
        Log.d(TAG, out.toString());
        out.println("欢迎来到聊天室");
        out.flush();
        while(!mIsServerDestroyed){
            String str = in.readLine();
            Log.d(TAG, "msg from client:"+str);
            if (str == null){
                break;
            }
            int i = new Random().nextInt(mDefinedMessages.length);
            String msg = mDefinedMessages[i];
            out.println(msg);
            out.flush();
            Log.d(TAG, "send : "+msg);
        }
        Log.d(TAG, "client quit...");
        MyUtils.close(in);
        MyUtils.close(out);
        client.close();
    }
}
  1. 活动
public class TcpClientActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "TCPClientActivity";
    private static final int MSG_RECEIVE_NEW_MSG = 1;
    private static final int MSG_SOCKET_CONNECTED = 2;

    private Button mSendButton;
    private TextView mMessageTextView;
    private EditText mMessageEditText;

    private PrintWriter mPrintWriter;
    private Socket mClientSocket;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RECEIVE_NEW_MSG:
                    mMessageTextView.setText(mMessageTextView.getText() + (String) msg.obj);
                    break;
                case MSG_SOCKET_CONNECTED:
                    mSendButton.setEnabled(true);
                    break;
                default:
                    break;
            }
            super.handleMessage(msg);
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tcp_client);
        Log.d(TAG, "onCreate");
        mMessageTextView = findViewById(R.id.msg_container);
        mSendButton = findViewById(R.id.send);
        mSendButton.setOnClickListener(this);
        mMessageEditText = findViewById(R.id.msg);
        Intent intent = new Intent(this, TCPServerService.class);
        startService(intent);
        Log.d(TAG, "onCreate");
        new Thread() {
            @Override
            public void run() {
                connectTCPServer();
            }
        }.start();
    }

    @Override
    protected void onDestroy() {
        if (mClientSocket != null) {
            try {
                //连接的关闭要先把流给杀死,然后才能关闭连接
                mClientSocket.shutdownInput();
                mClientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        super.onDestroy();
    }

    private void connectTCPServer() {
        Socket socket = null;
        while (socket == null) {
            try {
                socket = new Socket("localhost", 8888);
                mClientSocket = socket;

                mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                        socket.getOutputStream()
                )), true);
                mHandler.sendEmptyMessage(MSG_SOCKET_CONNECTED);

            } catch (IOException e) {
                e.printStackTrace();
                SystemClock.sleep(1000);
                Log.d(TAG, "connect server failed, retry...");
            }

        }
        Log.d(TAG, "connect server success. ");
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    mClientSocket.getInputStream()
            ));

            Log.d(TAG, br.toString());
            while (!TcpClientActivity.this.isFinishing()) {
                Log.d(TAG, "qian");
                String msg = br.readLine();
                Log.d(TAG, "hou");
                Log.d(TAG, "receiver:" + msg);
                if (msg != null) {
                    String time = fromatDateTime(System.currentTimeMillis());
                    final String showMsg = "server " + time + ":" + msg + "\n";
                    mHandler.obtainMessage(MSG_RECEIVE_NEW_MSG, showMsg)
                            .sendToTarget();
                }
            }
            Log.d(TAG, "quit...");
            MyUtils.close(mPrintWriter);
            MyUtils.close(br);
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String fromatDateTime(long time) {
        return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
    }


    @Override
    public void onClick(View v) {
        if (v == mSendButton) {
            Log.d(TAG, "点击了");
            final String msg = mMessageEditText.getText().toString();
            if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                new Thread(){
                    @Override
                    public void run() {
                        mPrintWriter.println(msg);
                    }
                }.start();
                mMessageEditText.setText("");
                String time = fromatDateTime(System.currentTimeMillis());
                final String showedMsg = "self" + time + ":" + msg + "\n";
                mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
            }
        }
    }
}
发表在 未分类 | 留下评论