Android: remote object ‘/data/data/com.myproject.walk’ does not exist

问题

在Android Studio/IDEA中的Device File Explorer想打开Android模拟器中的文件,有时候会提示上面的错误.究其原因是模拟器没有root.解决办法往下看.

Android LOGO
image-3216

处理问题

针对Android 8.0 +模拟器.

  1. 启动模拟器;
  2. 设置—系统—关于模拟设备;
  3. 连续鼠标左键点击”版本号”5次,直到提示”您已处于开发者模式!”;
  4. 返回上级目录;
  5. 点击进入”开发者选项”,会显示”开启”。往下拉,打开”USB调试”开关,出现弹框选择”确定”;
  6. 打开命令行工具,依次输入以下命令:

    adb root

    adb remount

成功的时候会依次提示:

C:\Users\xz> adb root

restarting adbd as root

C:\Users\xz> adb remount

remount succeeded

然后再次尝试就可以了.

Java 15:发布

很重要的提醒

Java 15即将于2020年9月15发布.

仅摘录部分更新特性.全部特性可以在 https://openjdk.java.net/projects/jdk/15/找到.

全部更新:

  • 爱德华兹曲线数字签名算法(EdDSA)
  • 密封课程(预览)
  • 隐藏的课程
  • 删除Nashorn JavaScript引擎
  • 重新实现旧版DatagramSocket API
  • 禁用和弃用偏置锁定
  • instanceof的模式匹配(第二预览)
  • ZGC:可扩展的低延迟垃圾收集器
  • 文字块
  • Shenandoah:低暂停时间的垃圾收集器
  • 删除Solaris和SPARC端口
  • 外部存储器访问API(第二个孵化器)
  • 记录(第二预览)
  • 弃用RMI激活以进行删除

java标志
image-3213

密封类或接口(预览)

密封类或接口可以用来限制只有特定的类或接口才能继承或实现它们.

用sealed修饰符来声明密封类.然后使用permits来指定允许扩展密封类的类.

示例:


public abstract sealed class Shape
	permits Circle, Rectangle, Square {}

同时,子类在不同的包中也是可以的:


	public abstract sealed class Shape
		permits com.example.polar.Circle,
				com.example.quad.Rectangle,
				com.example.quad.simple.Square {...}

如果都在同一个包中,则可以像下面这样声明:


abstract sealed class Shape { }

class Circle extends Shape { }
class Rectangle extends Shape { }
class Square extends Shape { }

密封类针对子类(由permits修饰符指定的类)有三个约束:

1. 密封类及其允许的子类必须属于同一模块,并且如果在未命名的模块中声明,则必须属于同一包。
2. 每个允许的子类都必须直接扩展密封的类。
3. 每个允许的子类都必须选择一个修饰符来描述其如何继续其父类发起的密封:
3.1 可以声明一个允许的子类,final以防止其在类层次结构中的进一步扩展。
3.2 可以声明一个允许的子类,sealed以允许其层次结构的一部分扩展到超出其密封超类所设想的范围,但以受限的方式。
3.3 可以声明一个允许的子类,non-sealed以便其层次结构的一部分恢复为未知子类可以扩展的扩展。(密封类不能阻止其允许的子类这样做。)

密封接口和类的使用方式类似:


	public sealed interface Expr
		permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {}

	public final class ConstantExpr implements Expr {}
	public final class PlusExpr implements Expr {}
	public final class TimesExpr implements Expr {}
	public final class NegExpr implements Expr {}

密封类与record可以配合使用:


	public sealed interface Expr
		permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {}

	public record ConstantExpr(int i) implements Expr {}
	public record PlusExpr(Expr a, Expr b) implements Expr {}
	public record TimesExpr(Expr a, Expr b) implements Expr {}
	public record NegExpr(Expr e) implements Expr {}

instanceof模式匹配(第二预览)

instanceof模式匹配在Java 14中新增,在Java 15中开启第二预览.功能方面没有任何更改.

文本块(Text Blocks)

文本块在Java 13中开启预览,Java 14开启第二预览.在Java 15中发布.在Java 15中文本块没有任何更改.

record(第二预览)

1. 内部声明record

Java 15中增加了在方法内部使用record:

本地记录是嵌套记录的一种特殊情况。像所有嵌套记录一样,本地记录也是隐式静态的。这意味着它们自己的方法无法访问封闭方法的任何变量;反过来,这避免了捕获立即封闭的实例,该实例将以静默方式将状态添加到记录中。本地记录是隐式静态的,这与不是隐式静态的本地类相反。实际上,局部类永远不会是静态的(隐式或显式),并且始终可以访问封闭方法中的变量。


List findTopMerchants(List merchants, int month) {
	// Local record
	record MerchantSales(Merchant merchant, double sales) {}

	return merchants.stream()
		.map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
		.sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
		.map(MerchantSales::merchant)
		.collect(toList());
}

2. record的注解

如下的注解示例:


public final class Card {
	private final @MyAnno Rank rank;
	private final @MyAnno Suit suit;
	@MyAnno Rank rank() { return this.rank; }
	@MyAnno Suit suit() { return this.suit; }

}

可以优化为:



public record Card(@MyAnno Rank rank, @MyAnno Suit suit) { }

返回到record上的注解,这些注解出现在适用的相应程序点处。换句话说,传播是在程序员使用@Target 元注解的控制下进行的。传播规则是系统且直观的,并且遵循所有适用的规则:

1. 如果记录组件上的注解适用于字段声明,则注解将出现在相应的private字段上。
2. 如果记录组件上的注解适用于方法声明,则该注解将出现在相应的访问器方法上。
3. 如果记录组件上的注解适用于形式参数,则如果未显式声明注解,则该注解将出现在规范构造函数的相应形式参数上;如果显式声明,则注解将出现在紧凑构造函数的相应形式参数上。
4. 如果记录组件上的注解适用于某个类型,则传播规则与声明注解的传播规则相同,不同之处在于,该注解出现在相应的类型用途上而不是声明上。

Flutter: 为图片添加说明信息

实现效果

如下:

See the Pen
图片上添加说明文字
by pruidong (@pruidong)
on CodePen.

image-3209

实现

在Container布局中,将图片放在BoxDecoration中.这样图片自然就在底层了.之后在Positioned中进行描述信息的处理,样式,位置这些都可以完全自定义.


import 'package:flutter/material.dart';



/// 在图片上添加说明文字.
/// 在图片上使用浅黑色背景,添加说明文字.
///
///
/// prd
/// 2020-07-26


void main() {
  runApp(
    MaterialApp(
      home: MyWidget(),
    ),
  );
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
            padding: EdgeInsets.all(2.0),
            decoration: BoxDecoration(
              image: DecorationImage(
                image: NetworkImage( 'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',),
                fit: BoxFit.cover,
              ),
            ),
            child: Stack(
              children: [
                Positioned(
                  left: 2.0,
                  right: 2.0,
                  bottom: 2.0,
                  height: 70.0,
                  child: SingleChildScrollView(
                    child:Container(
                    padding: EdgeInsets.all(5.0),
                    decoration: BoxDecoration(
                        color: Colors.black45,
                        borderRadius: BorderRadius.all(Radius.circular(5.0))),
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.start,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          "这是一段说明文字",
                          style: TextStyle(color: Colors.white,fontSize:22.0,),
                        ),
                        Text(
                          "可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字,可以滚动详细说明文字.",
                          style: TextStyle(color: Colors.white),
                        )
                      ],
                    ),
                  ),
                  )
                ),
              ],
            ),
          ),
    );
  }
}

Flutter: 轮播组件

实现一个轮播

在PC端实现轮播一般都是直接使用JavaScript库进行实现.但在Flutter上想要实现一个轮播,可能只有自己写一下了.

轮播的特点:

1. 可以自动播放;
2. 图片可以左右滑动(使用Flutter自带的PageView初始只能向右滑动,不能向左);
3. 可以定义自动播放的间隔时间.

大概就这么几个特点,可以实现个简单的例子.

image-3203

功能部分

目前仅实现简单功能:
1. 可以自定义网络图片或本地图片(可以混用本地图片和网络图片),
2. 自定义图片切换时间,
3. 自定义是否开启自动轮播.

上述几条可以满足一个简单轮播的需要,如果需要更多功能.可以在此基础上增加.

image-3204

源码

完整代码: Gitee仓库


/// 轮播组件
///
///  目前仅实现简单功能:
///   1. 可以自定义网络图片或本地图片(可以混用本地图片和网络图片),
///   2. 自定义图片切换时间,
///   3. 自定义是否开启自动轮播.
///
///  调用示例:
///
///  ```dart
///  static const List _images = [
///   'assets/images/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
///   'assets/images/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
///   'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
///   'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
///   'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
///   'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
///   'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg',
///   'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
///   'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
///   'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg'
///  ];
///
/// ......
///
/// SizedBox(
//            width: double.infinity,
//            height: 200,
//            child: Carousel(
//              _images,
//              autoPlayer: false,
//            ),
//          ),
///
/// ```
///
///
///
///
class Carousel extends StatefulWidget {
  Carousel(
    this.imagesList, {
    Key key,
    this.autoPlayer = true,
    this.seconds = 5,
  })  : assert(imagesList.isNotEmpty),
        assert(seconds > 2),
        super(key: key);

  final int _maxPage = 2000000000;

  /// 轮播切换间隔秒数.
  final int seconds;

  /// 是否自动播放
  final bool autoPlayer;

  final List imagesList;

  @override
  _CarouselState createState() => new _CarouselState();
}

class _CarouselState extends State {

  PageController _pageController;
  Timer _timer;

  var _currIndex = 0;

  @override
  void initState() {
    super.initState();
    if (widget.autoPlayer) {
      startTimeout();
    }
    _pageController = new PageController(initialPage: widget._maxPage);
  }

  @override
  void dispose() {
    super.dispose();
    _pageController.dispose();
    if (_timer != null) {
      _timer.cancel();
    }

  }

  void startTimeout() {
    var timeout = Duration(seconds: widget.seconds);
    Timer.periodic(timeout, (Timer timer) {
      var tempIndex = _currIndex + 1;
      if (tempIndex >= widget.imagesList.length) {
        tempIndex = 0;
      }
      setState(() {
        _timer = timer;
        _currIndex = tempIndex;
      });
    });
  }

  List getBottomIcon() {
    var forLength = widget.imagesList.length;
    var res = List();
    for (var i = 0; i < forLength; i++) {
      if (_currIndex == i) {
        res.add(GestureDetector(
          /// 选中时的图标,可以更换为图片.
          child: Icon(Icons.brightness_1),
          onTap: () {
            setState(() {
              _currIndex = i;
            });
          },
        ));
      } else {
        res.add(GestureDetector(
          /// 未选中时的图标,可以更换为图片.
          child: Icon(Icons.panorama_fish_eye),
          onTap: () {
            setState(() {
              _currIndex = i;
            });
          },
        ));
      }
    }
    return res;
  }

  @override
  Widget build(BuildContext context) {
    var width = MediaQuery.of(context).size.width;
    return Scaffold(
      body: PageView.builder(
          controller: _pageController,
          onPageChanged: (int index) {
            var renderIndex = index - widget._maxPage;
            renderIndex = renderIndex % widget.imagesList.length;
            if (renderIndex < 0) {
              renderIndex += widget.imagesList.length;
            }
            setState(() {
              _currIndex = renderIndex;
            });
          },
          itemBuilder: (BuildContext context, int index) {
            var renderIndex = _currIndex - widget._maxPage;
            renderIndex = renderIndex % widget.imagesList.length;
            if (renderIndex < 0) {
              renderIndex += widget.imagesList.length;
            }
            return Container(
                child: Stack(children: [
              Center(
                child: widget.imagesList[_currIndex].startsWith("https://") ||
                        widget.imagesList[_currIndex].startsWith("http://")
                    ? CachedNetworkImage(
                        imageUrl: widget.imagesList[_currIndex],
                        fit: BoxFit.fill,
                        width: width,
                        height: double.infinity,
                        placeholder: (context, url) =>
                            CircularProgressIndicator(),
                        errorWidget: (context, url, error) => Icon(Icons.error),
                      )
                    : FadeInImage(
                        image: AssetImage(widget.imagesList[_currIndex]),
                        placeholder: AssetImage("assets/images/loading.gif"),
                        fit: BoxFit.fill,
                        width: width,
                        height: double.infinity,
                      ),
              ),
              Positioned(
                  left: 2.0,
                  right: 2.0,
                  bottom: 10.0,
                  child: Container(

                      /// padding: EdgeInsets.all(5.0),
                      child: widget.imagesList.length > 10
                          ? SingleChildScrollView(
                              scrollDirection: Axis.horizontal,
                              child: Row(
                                mainAxisAlignment: MainAxisAlignment.center,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: getBottomIcon(),
                              ),
                            )
                          : Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: getBottomIcon(),
                            )))
            ]));
          }),
    );
  }
}

Flutter: 无限滚动

为什么需要无限滚动

之前做过一个横向图片展示的页面,发现就需要无限滚动(可以往左滑,也可以往右滑).第一时间首先想到的是用PageView,但是发现PageView不支持往左滑动,只能往右滑.这明显限制了功能的实用性.

于是在网上参考了一些教程之后,有了下面的代码.

image-3198

原理

原理其实很简单,就是初始化的时候给PageView设置一个特别大的值.这样无论往左或者往右都可以滑动.通过滑动的值,计算实际的下标,进行展示对应的数据即可.

在线运行效果

源码

源码如下:


import 'package:flutter/material.dart';

/// run to dartpad.dev or dartpad.cn
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: HomePage(),
      );
}

int maxPage = 2000000000;

class HomePage extends StatelessWidget {
  static const List<Color> _colors = [Colors.blue, Colors.green, Colors.red];

  final pageController = new PageController(initialPage: maxPage);

  @override
  Widget build(BuildContext context) => Scaffold(
        body: PageView.builder(
          controller: pageController,
          itemBuilder: (BuildContext context, int index) {
            var renderIndex = index - maxPage;
            renderIndex = renderIndex % _colors.length;
            if (renderIndex < 0) {
              renderIndex += _colors.length;
            }
            return Container(
              color:_colors[index % _colors.length],
                child: Center(
                    child: Text("text $renderIndex, default Index: $index",
                        style: TextStyle(
                            fontSize: 36.0,
                            color: Colors.white))));
          },
        ),
      );
}